Usage
Now that you have setup everything, lets get started using
fastapi-login.
Login route
First we need to create a login route for our users.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_login.exceptions import InvalidCredentialsException
app = FastAPI()
def query_user(email):
return {"email": email, "password": "password"}
@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
return {"status": "Success"}
Note
We use the same url("/login") we have used as the token_url argument,
when initiating our LoginManager instance
Returning token
Now that we have created a login route, we want to return a token, which can than be used to access protected routes.
@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
access_token = manager.create_access_token(data={"sub": email})
return {"access_token": access_token}
Bug
LoginManager expects the parameter used in your LoginManager.user_loader
function to be in the sub field of the token. This conforms the official
specification of json web tokens.
Usage as a dependency
After the user has a way to obtain an access_token, the LoginManager instance
can be used as a dependency.
@app.get("/protected")
def protected_route(user=Depends(manager)):
return {"user": user}
Note
user in this case will be whatever the LoginManager.user_loader decorated function returns.
In case user is None the LoginManager.not_authenticated_exception
will be thrown, this results by default to
fastapi_login.exceptions.InvalidCredentialsException.
If a custom exception has been set by the user it will be
raised instead.
Warning
By default the token is expected to be in the Authorization header
value fo the request and of the following format:
Bearer <token>
Returning None instead of raising an exception
In case that one wishes to handle the case of an unauthorized user, differently than returning an exception
since version 1.9.0 it is possible to use the optional method:
@app.get("/protected")
def protected_route(user=Depends(manager.optional)):
if user is None:
# Do something ...
pass
return {"user": user}
Note
This catches all exceptions that are raised while trying to authenticate a user.
This might lead to cases where valid exception from e.g. the database or FastAPI are caught.
If you aware of a solution that allows to catch custom exception that are subclasses of HTTPException
or Exception, please let me know. See also #47
and #78 for more discussion on this topic.