pythonhttp-redirectfastapihttpresponsesetcookie

FastAPI_Login RedirectResponse after authorization fails


I have a simple login form that is opened using a GET request, and the user submits the form with a POST request. After successful authentication, I redirect the user to the main page of my web application using a RedirectResponse object.

However, despite the successful redirection, the user is not authorized on the main page, as if the authentication cookie was not set. There are no error messages displayed.

Here's the code for my login route:

@router.post('/login')
def login(response: Response, 
          data: OAuth2PasswordRequestForm = Depends(), 
          db: Session = Depends(get_db)):
    email = data.username
    password = data.password

    user = db.query(DbUser).filter(DbUser.email == email).first()

    if not user:
        # you can return any response or error of your choice
        raise InvalidCredentialsException

    if not Hash.verify(user.password, password):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
            detail='Incorrect password')

    token = manager.create_access_token(
        data={'sub': email}
    )
    resp = RedirectResponse(url='/post/stat/all',status_code=status.HTTP_302_FOUND)
    manager.set_cookie(response, token)
    return resp

However, if I change resp = RedirectResponse(... to resp = 'ok', then everything works fine—just without a URL redirection.

Tried also with no luck:

return templates.TemplateResponse("/login/success.html",{"request": request})

What would the issue be and the way to fix it?


Solution

  • This is due to specifying the Response object instead of RedirectResponse in the manager.set_cookie() method. As per your example:

    @router.post('/login')
    def login(response: Response,
              ^^^^^^^^ 
              ...):
    
        ...
    
        resp = RedirectResponse(url='/post/stat/all', status_code=status.HTTP_302_FOUND)
        ^^^^
        manager.set_cookie(response, token)
                           ^^^^^^^^
        return resp
    

    This is why returning OK or anything other than a custom Response object works, as you noticed. Hence, since you are instead returning a custom RedirectResponse, you should set the cookie for that RedirectResponse instance:

    @router.post('/login')
    def login(data: OAuth2PasswordRequestForm = Depends(),
              db: Session = Depends(get_db)):
        ...
    
        resp = RedirectResponse(url='/post/stat/all', status_code=status.HTTP_302_FOUND)
        manager.set_cookie(resp, token)
        return resp
    

    Related answer can be found here as well. A complete working example can also be found in this answer.