pycasbin / fastapi-authz

Use Casbin in FastAPI, Casbin is a powerful and efficient open-source access control library.
https://github.com/casbin/pycasbin
Apache License 2.0
149 stars 15 forks source link

Encounterd with preflight request failure when using fastapi-authz with axios #15

Closed jaystone776 closed 2 years ago

jaystone776 commented 2 years ago

Code

backend

fastapi 0.68 main.py

app.add_middleware(CORSMiddleware,
        allow_origins=["http://localhost:8080"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
app.add_middleware(CasbinMiddleware, enforcer=enforcer)
app.add_middleware(AuthenticationMiddleware, backend=JWTAuth())

app.include_router(api_router, prefix=settings.API_V1_STR)

rbac.py for AuthenticationMiddleware backend, and Casbin enforcer

class JWTAuth(AuthenticationBackend):
    async def authenticate(self, request):
        if "Authorization" not in request.headers:
            return None

        if "Authorization" in request.headers and not request.headers["Authorization"].startswith("Bearer"):
            return None

        if request.headers["Authorization"].startswith("Bearer"):
            auth = request.headers["Authorization"]

        try:
            scheme, token = auth.split()
            payload = jwt.decode(
                token, settings.SECRET_KEY, algorithms=["HS256"])

            email = payload["email"]
        except (jwt.JWTError, ValidationError):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Cannot validate credential",
            )

        return AuthCredentials(["authenticated"]), SimpleUser(email)

enforcer = casbin.Enforcer(
    '../app/core/rbac_rules/rbac_model.conf',
    '../app/core/rbac_rules/rbac_policy.csv'
)

endpoints/users.py - read_user_me

@router.get("/me", response_model=schemas.User)
def read_user_me(
    db: Session = Depends(deps.get_db),
    current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
    return current_user

rbac_policy.csv

p, *, /api/v1/openapi.json, GET
p, *, /docs, *
p, *, /api/v1/login/access-token, *
p, admin, *, *

g, a@b.com, admin,

frontend

vue.sj 3 Compositional API + Quasar 2 + axios 0.21.1 + vuex 4.0.1 store/auth/actions.js

export const doLogin = async ({ commit, dispatch }, formData) => {
  await api.post("/login/access-token", formData).then((response) => {
    const token = response.data;
    commit("setToken", token);
    api.defaults.headers.common["Authorization"] =
      "Bearer " + token.access_token;
    dispatch("getMe", token);
  });
};

export const getMe = async ({ commit }, token) => {
  await api.get("/users/me", token).then((response) => {
    commit("setMe", response.data);
  });
};

Description

The problem is Axios (api) sends Authorization info with the request header, then the browser needs to use a preflight request to check that request. But the preflight check failed, and the request headers passed to the backend would be missing the Authorization (Bearer + Token), which is causing the Authentication error.

And then, with no Authentication, there would be no user displayname, and the is_authenticated would be false. Without the username, Casbin won't let anonymous users use OPTIONS on /api/v1/users/me.

image

The browser shows the error message as below. But I guess is not the CORS policy that blocked the access, it is actually Casbin who blocked the access.

image

My Solution

I tried to add permission for anonymous users to use OPTIONS on /api/v1/users/me, and that worked. Finally, Axios can get user info from the backend.

rbac_policy.csv

p, *, /api/v1/openapi.json, GET
p, *, /docs, *
p, *, /api/v1/login/access-token, *
p, anonymous, /api/v1/users/me, OPTIONS  # see this line
p, admin, *, *

g, a@b.com, admin,

But I'm not sure it's the right way to do so, because after logging in, I still had to use the anonymous user to get permission, it feels weird and insecure.

I'm wondering is there any better way to fix it, if anyone can help me to improve, thanks.

casbin-bot commented 2 years ago

@ffyuanda @Zxilly @techoner @elfisworking

github-actions[bot] commented 2 years ago

:tada: This issue has been resolved in version 0.1.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: