Redirect to login page if user not logged in using FastAPI-Login package
Asked Answered
A

1

4

I would like to redirect users to the login page, when they are not logged in.

Here is my code:

from fastapi import (
    Depends,
    FastAPI,
    HTTPException,
    status,
    Body,
    Request
)
from fastapi.encoders import jsonable_encoder
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi.responses import HTMLResponse, RedirectResponse
import app.models as models
import app.database as database
from datetime import datetime, timedelta
from jose import JWTError, jwt
from starlette.responses import FileResponse
from fastapi_login import LoginManager
from fastapi_login.exceptions import InvalidCredentialsException
from fastapi import Cookie
import re

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
manager = LoginManager(SECRET_KEY, token_url="/auth/login", use_cookie=True)
manager.cookie_name = "token"


@app.get("/")
@app.get("/item")
async def read_index(user=Depends(manager)):
    try:
        return FileResponse('item.html')
    except status.HTTP_401_UNAUTHORIZED:
        return RedirectResponse(url="/login", status_code=status.HTTP_302_FOUND)

However, when I access this page: localhost:8000/item, I get the following:

{"detail":"Not authenticated"}
Accipiter answered 7/9, 2022 at 5:48 Comment(2)
Where did status come from? What does login manager? I’d login manager raises, the endpoint function is never called.Cycloparaffin
I have edited the code. I do not know what I should do nextAccipiter
A
7

From the code snippet you provided, you seem to be using the (third-party) FastAPI-Login package. Their documentation suggests using a custom Exception on the LoginManager instance, which can be used to redirect the user to the login page, if they are not logged in.

Working example:

The authentication below is based on cookies, but you could pass the token in the Authorization header as well. Navigate to /protected route when you are not logged in for the redirection to take effect.

from fastapi import FastAPI, Depends, Request, Response, status
from starlette.responses import RedirectResponse, HTMLResponse, JSONResponse
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_login.exceptions import InvalidCredentialsException
from fastapi_login import LoginManager

class NotAuthenticatedException(Exception):
    pass
    
app = FastAPI()
SECRET = "super-secret-key"
manager = LoginManager(SECRET, '/login', use_cookie=True, custom_exception=NotAuthenticatedException)


DB = {
    'users': {
        '[email protected]': {
            'name': 'John Doe',
            'password': 'hunter2'
        }
    }
}

def query_user(user_id: str):
    return DB['users'].get(user_id)


@manager.user_loader()
def load_user(user_id: str):
    user = DB['users'].get(user_id)
    return user
    
 
@app.exception_handler(NotAuthenticatedException)
def auth_exception_handler(request: Request, exc: NotAuthenticatedException):
    """
    Redirect the user to the login page if not logged in
    """
    return RedirectResponse(url='/login')
    

@app.get("/login", response_class=HTMLResponse)
def login_form():
    return """
    <!DOCTYPE html>
    <html>
       <body>
          <form method="POST"  action="/login">
             <label for="username">Username:</label><br>
             <input type="text" id="username" name="username" value="[email protected]"><br>
             <label for="password">Password:</label><br>
             <input type="password" id="password" name="password" value="hunter2"><br><br>
             <input type="submit" value="Submit">
          </form>
       </body>
    </html>
    """

    
@app.post('/login')
def login(data: OAuth2PasswordRequestForm = Depends()):
    email = data.username
    password = data.password
    user = query_user(email)
    if not user:
        # you can return any response or error of your choice
        raise InvalidCredentialsException
    elif password != user['password']:
        raise InvalidCredentialsException

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


@app.get('/protected')
def protected_route(user=Depends(manager)):
    return {'user': user}
Allowable answered 9/9, 2022 at 13:0 Comment(1)
it is a good practice to redirect user to the next endpoint after login and get access token. But when you the url address bar does not change to the new one and stay the same as before. The second issue is about working with swagger. I would like to authenticate with swagger of fastapi. it needs to get a token directly.Verona

© 2022 - 2024 — McMap. All rights reserved.