Advanced usage
Cookies
By default LoginManager
expects the token to be in the Authorization
header. However cookie support can be enabled.
from fastapi_login import LoginManager
manager = LoginManager(
'secret', '/login',
use_cookie=True
)
For convenince a set_cookie
method is provided, which sets the cookie, using
LoginManager.cookie_name
and the recommended HTTPOnly
flag.
@app.post('/login')
def login(response: Response):
...
token = manager.create_access_token(
data=dict(sub=user.email)
)
manager.set_cookie(response, token)
return response
Configuration
By default, LoginManager
looks for a cookie with the name access-token
,
this can be changed using the cookie_name
argument, when creating the instance.
manager = LoginManager(
...,
cookie_name='custom-cookie-name'
)
use_header
can be set
to false on initialization.
from fastapi_login import LoginManager
manager = LoginManager(
'secret', '/login',
use_cookie=True,
use_header=False
)
Exception handling
Sometimes it is needed to run some code if a user is not authenticated,
this can achieved, by setting a custom Exception
on the LoginManager
instance.
from fastapi import Request
from starlette.responses import RedirectResponse
class NotAuthenticatedException(Exception):
pass
# Before version 1.7.0
# manager.not_authenticated_exception = NotAuthenticatedException
# The updated way
manager = LoginManager(
...,
custom_exception=NotAuthenticatedException
)
@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')
Now whenever there is no, or an invalid token present in the request, your exception will be raised, and the exception handler will be executed.
More to writing exception handlers can be found in the official documentation of FastAPI
Token expiry
By default token's expire after 15 minutes. This can be changed using the expires
argument in the create_access_token
method.
# expires after 12 hours
long_token = manager.create_access_token(
data=data, expires=timedelta(hours=12)
)
manager = LoginManager(
...,
default_expiry=timedelta(hours=12)
)
Middleware
Optionally a LoginManager
instance can also be added as a middleware.
It's important to note that request.state.user
is set to None
if
no (valid) token is present in the request.
from fastapi.requests import Request
manager.useRequest(app)
@app.route('/showcase')
def showcase(request: Request):
# None if unauthorized
user = request.state.user
from fastapi.requests import Request
from fastapi.exceptions import HTTPException
def is_admin(request: Request):
user = request.state.user
if user is None:
raise HTTPException(401)
# assuming our user object has a is_admin property
elif not user.is_admin:
raise HTTPException(401)
else:
return user
OAuth2 scopes
In addition to normal token authentication, OAuth2 scopes can be used to restrict access to certain routes.
@app.get("/scoped/route")
def scoped_route(user=Security(manager, scopes=["required", "scopes", "here"])):
...
fastapi.Depends
fastapi.Security
is used.
In order to give your token the required scopes LoginManager.create_access_token
has a scopes
parameter.
In order for the scopes to show up in the OpenAPI docs, your scopes need to be passed
as an argument when instantiating LoginManager.
Predefining additional user_loader
arguments
The LoginManager.user_loader
can also take arguments which will be passed on the
callback
@manager.user_loader(db_session=...)
def query_user(user_id: str, db_session):
"""
Get a user from the db
:param user_id: E-Mail of the user
:param db_session: The currently active connection to the database
:return: None or the user object
"""
return db_session.get(user_id)
Callable as extra argument
Before version 1.7.0 empty parentheses were not needed after the decorator. To provide backwards compatibility it's still possible to omit them as of v1.7.1. Because of this it's however not possible to pass a callable object as the first extra argument.
This will not work:
def some_callable_object(...):
...
@manager.user_loader(some_callable_object)
def load_user(email, some_callable):
...
@manager.user_loader(some_callable=some_callable_object)
def load_user(email, some_callable)
Asymmetric algorithms
Thanks to filwaline in addition to symmetric keys, RSA can also be used to sign the tokens.
Required dependencies
The cryptography packages is required for this. Run the following command to install all the required dependencies.
pip install fastapi-login[asymmetric]
Supported algorithms
Currently RS256
is the only asymmetric algorithm supported by the package.
If you need another algorithm, please open an
issue and I will
consider adding it.
To use the asymmetric algorithm choose algorithm="RS256"
when initiating LoginManager
.
LoginManager(
secret="...", token_url="...",
algorithm="RS256"
)
If your private key is not password protected the usage of the package stays exactly the same.
Hint
You don't need to pass your public key explicitly, as it can be derived from the private key.
However, if you used a password during the key generation the syntax changes slightly.
manager = LoginManager(
secret={"private_key": "your_rsa_key", "password": "your_password_for_the_key"},
token_url="...",
algorithm="RS256"
)
private_key
and the password
fields set.