FastAPI Auth Middleware
We at Code Specialist love FastAPI for its simplicity and feature-richness. Though we were a bit staggered by the poor documentation and integration
of auth-concepts. That’s why we wrote a FastAPI Auth Middleware. It integrates seamlessly into FastAPI applications and requires minimum configuration. It is built
upon Starlette and thereby requires no dependencies you do not have included anyway.
Caution: This is a middleware to plug in existing authentication. Even though we offer some sample code, this package assumes you already have a way to generate and verify
whatever you use, to authenticate your users. In most of the usual cases this will be an access token or bearer. For instance as in OAuth2 or Open ID Connect.
Install
pip install fastapi-auth-middleware
Why FastAPI Auth Middlware?
- Application or Route scoped automatic authorization and authentication with the perks of dependency injection (But without inflated signatures due to
Depends()
) - Lightweight without additional dependencies
- Easy to configure
- Easy to extend and adjust to specific needs
- Plug-and-Play feeling
Usage
The usage of this middleware requires you to provide a single function that validates a given authorization header. The middleware will extract the content of the Authorization
HTTP header and inject it into your function that returns a list of scopes and a user object. The list of scopes may be empty if you do not use any scope based concepts. The user
object must be a BaseUser
or any inheriting class such as FastAPIUser
. Thereby, your verify_authorization_header
function must implement a signature that contains a string as
an input and a Tuple
of a List of strings
and a BaseUser
as output:
from typing import Tuple, List
from fastapi_auth_middleware import FastAPIUser
from starlette.authentication import BaseUser
...
def verify_authorization_header(auth_header: str) -> Tuple[List[str], BaseUser]:
user = FastAPIUser(first_name="Code", last_name="Specialist", user_id=1) # Usually you would decode the JWT here and verify its signature to extract the 'sub'
scopes = [] # You could for instance use the scopes provided in the JWT or request them by looking up the scopes with the 'sub' somewhere
return scopes, user
This function is then included as an argument when adding the middleware to the app.
from fastapi import FastAPI
from fastapi_auth_middleware import AuthMiddleware
...
app = FastAPI()
app.add_middleware(AuthMiddleware, verify_authorization_header=verify_authorization_header)
After adding this middleware, all requests will pass the verify_authorization_header
function and contain the scopes as well as the user object as injected dependencies. You may
then verify users posses the required scopes with requires
:
from starlette.authentication import requires
...
@app.get("/")
@requires(["admin"]) # Will result in an HTTP 401 if the scope is not matched
def some_endpoint():
...
You are also able to use the user object you injected on the request
object:
from starlette.requests import Request
...
@app.get('/')
def home(request: Request):
return f"Hello {request.user.first_name}" # Assuming you use the FastAPIUser object
Examples
Simple
simple.py
from typing import Tuple, List
import uvicorn
from fastapi import FastAPI
from starlette.requests import Request
from fastapi_auth_middleware import AuthMiddleware, FastAPIUser
# The method you have to provide
def verify_authorization_header(auth_header: str) -> Tuple[List[str], FastAPIUser]:
user = FastAPIUser(first_name="Code", last_name="Specialist", user_id=1) # Usually you would decode the JWT here and verify its signature to extract the 'sub'
scopes = [] # You could for instance use the scopes provided in the JWT or request them by looking up the scopes with the 'sub' somewhere
return scopes, user
app = FastAPI()
app.add_middleware(AuthMiddleware, verify_authorization_header=verify_authorization_header) # Add the middleware with your verification method to the whole application
@app.get('/') # Sample endpoint (secured)
def home(request: Request):
return request.user
if __name__ == '__main__':
uvicorn.run('simple:app', host="0.0.0.0", port=8080) # Starts the uvicorn ASGI
Simple with scopes
simple_with_scopes.py
from typing import Tuple, List
import uvicorn
from fastapi import FastAPI
from starlette.authentication import requires
from starlette.requests import Request
from fastapi_auth_middleware import AuthMiddleware, FastAPIUser
# The method you have to provide
def verify_authorization_header(auth_header: str) -> Tuple[List[str], FastAPIUser]:
user = FastAPIUser(first_name="Code", last_name="Specialist", user_id=1) # Usually you would decode the JWT here and verify its signature to extract the 'sub'
scopes = ["admin"] # You could for instance use the scopes provided in the JWT or request them by looking up the scopes with the 'sub' somewhere
return scopes, user
app = FastAPI()
app.add_middleware(AuthMiddleware, verify_authorization_header=verify_authorization_header) # Add the middleware with your verification method to the whole application
@app.get('/home') # Sample endpoint (secured)
@requires("admin") # Requires the role 'admin' (Will succeed)
def home(request: Request):
return request.user # Returns the user object that is injected into the request. The FastAPIUser in this case
@app.get('/poweruser') # Sample endpoint (secured)
@requires(["admin", "poweruser"]) # Requires the roles 'admin' and 'poweruser' (Will fail)
def poweruser(request: Request):
return request.user # Returns the user object that is injected into the request. The FastAPIUser in this case
if __name__ == '__main__':
uvicorn.run('simple_with_scopes:app', host="0.0.0.0", port=8080) # Starts the uvicorn ASGI
Protected and Unprotected Endpoints
Currently, there is no support for router level middleware in FastAPI. However, Starlette currently shifts
toward supporting this feature and may support it soon. Once Starlette includes this and FastAPI adopts it, there will be a more
elegant solution to this. But the current solution is to mount multiple apps instead of routers:
multiple.py
from typing import Tuple, List
import uvicorn
from fastapi import FastAPI
from starlette.requests import Request
from fastapi_auth_middleware import AuthMiddleware, FastAPIUser
# The method you have to provide
def verify_authorization_header(auth_header: str) -> Tuple[List[str], FastAPIUser]:
user = FastAPIUser(first_name="Code", last_name="Specialist", user_id=1) # Usually you would decode the JWT here and verify its signature to extract the 'sub'
scopes = ["admin"] # You could for instance use the scopes provided in the JWT or request them by looking up the scopes with the 'sub' somewhere
return scopes, user
users_app = FastAPI()
users_app.add_middleware(AuthMiddleware, verify_authorization_header=verify_authorization_header) # Add the middleware with your verification method to the whole application
@users_app.get('/') # Sample endpoint (secured)
def home(request: Request):
return request.user # Returns the user object that is injected into the request. The FastAPIUser in this case
public_app = FastAPI()
@public_app.get('/home') # Sample endpoint (not secured)
def home(request: Request):
return 'Hello World'
app = FastAPI()
app.mount(path="/user", app=users_app) # Expects an authorization header, due to the auth middleware
app.mount(path="/", app=public_app) # Does not use any middleware
if __name__ == '__main__':
uvicorn.run('multiple:app', host="0.0.0.0", port=8080) # Starts the uvicorn ASGI
This will result in an protected endpoint at http:localhost:8080/user/
and an unprotected one at http:localhost:8080/home/
.