pythonfastapipyjwt

Authenticated and Non-authenticated Verification Path in FastAPI


So using FastAPI PyJWT, I'm struggling to make this work

user_dep = Annotated[Dict,Depends(api.get_current_user)]
@app.get('/')
async def home(request: Request,user:user_dep=Optional[models.AuthorizedUser]):
    print(user)
    if user is not None:
        return RedirectResponse(url='/dashboard/')
    return templates.TemplateResponse('home.html',context={'request':request})

Intended functionality: If the user is already logged in, they should automatically be redirected to the intended page I want.

Running this exact piece of code and when I hit the / path, it returns 401 Unauthorized error, which is not what I currently want for this one.

async def get_current_user(token: Annotated[str,Depends(oauth2_bearer)]):
       try:
              payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
              username: str = payload.get('username')
              user_id: int = payload.get('id')
              rank: str = payload.get('rank')
              division: str = payload.get('division')
              if username is None or user_id is None:
                     return None
                     #raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate user")
              return {'username': username, 'id': user_id, 'rank': rank, 'division':division}
       except PyJWTError:
              return None
              #raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")

Is there a possibility I am missing some steps, or anything?

I've tried everything, even removing doing the following, but nothing seems to work:

@app.get('/')
async def home(request: Request,user:user_dep=None):
    print(user)
    if user is not None:
        return RedirectResponse(url='/dashboard/')
    return templates.TemplateResponse('home.html',context={'request':request})
@app.get('/')
async def home(request: Request):
        user = await user_dep()
    print(user)
    if user is not None:
        return RedirectResponse(url='/dashboard/')
    return templates.TemplateResponse('home.html',context={'request':request})

Solution

  • OAuth2PasswordBearer will (by default) generate a 401 error when an Authorization header is not present, so your dependency code never actually runs (so you can't return None).

    To change the behavior you can supply the parameter auto_error when creating your OAuth2PasswordBearer instance.

    By default, if no HTTP Auhtorization header is provided, required for OAuth2 authentication, it will automatically cancel the request and send the client an error.

    If auto_error is set to False, when the HTTP Authorization header is not available, instead of erroring out, the dependency result will be None.

    This is useful when you want to have optional authentication.

    It is also useful when you want to have authentication that can be provided in one of multiple optional ways (for example, with OAuth2 or in a cookie).

    You then check if token is None before attempting to decode it in your authentication function.

    However, be aware that this will also make all other endpoints available if no token is present, just without user information. A solution to this is to define two get_current_user functions, one get_current_user and one get_optional_user, which in turn either can depend on two differently configured instances of OAuth2PasswordBearer, or you can handle the None case for the token in your get_current_user function and raise a 401 if the token isn't present yourself.