Reference#

Utility classes to use for authentication in FastAPI.

The purpose of this module is to reduce the boilerplate authentication code written for FastAPI. It is based on the code that you can find at OAuth2 with Password (and hashing), Bearer with JWT tokens.

class Authenticator(key: str, /, algorithm: str = 'HS256', cookie_name: str = 'fastapi_token', path: str = 'token', exp: datetime.timedelta = datetime.timedelta(seconds=3600), public_key=None)[source]#

Provides authentication for FastAPI endpoints.

Here’s an example of creating the authenticator from a secret key stored in an environment variable.

import os
from jwtdown_fastapi import Authenticator


class MyAuth(Authenticator):
    # Implement the abstract methods


auth = MyAuth(os.environ["SECRET_KEY"])
Parameters
  • key (str) – The cryptographically strong signing key for JWTs. If using certificates, provide the private key in the form of a string of its contents.

  • algorithm (str) – The algorithm to use to sign JWTs. Defaults to jose.constants.ALGORITHMS.HS256. If you are using public-private keys, use jose.constants.ALGORITHMS.RS256

  • cookie_name (str) – The name of the cookie to set in the browser. Defaults to the value of fastapi_token.

  • path (str) – The path that authentication requests will go to. Defaults to “token”.

  • public_key – If using certificates, provide the public key in the form of a string of its contents.

COOKIE_NAME = 'fastapi_token'#

The override value for the cookie name set by the authenticator.

You can override the cookie name used by the authenticator with code like this.

import os
from jwtdown_fastapi import Authenticator


class MyAuth(Authenticator):
    COOKIE_NAME = "custom_cookie_name"

    # Implement the abstract methods
abstract async get_account_data(username: str, account_getter: Any) Optional[Union[pydantic.main.BaseModel, dict]][source]#

Get the user based on a username.

You MUST implement this method in your custom class!

This method uses the account_getter returned from get_account_getter as the third argument. It’s the job of this method to get the account data for the provided username. username can be an email!

def get_account_data(
    self,
    username: str,
    account_repo: AccountRepository,
) -> Account:
    return account_repo.get(username)
Parameters
  • username (str) – This is the value passed as the username in the log in form. It is the value that uniquely identifies a user in your application, such as a username or email.

  • account_getter (Optional[Any]) – Whatever thing you returned from account_getter.

Returns

account_data – If the account information exists, it should return a Pydantic model or dictionary. If the account information does not exist, then this should return None.

Return type

Optional[Union[BaseModel, dict]]

Converts account data to a dictionary

This default implementation can accept either a Pydantic model or a dictionary. The value _must_ contain the “email” property/key for the subject claim of the JWT that is generated from this data.

If the resulting dictionary contains a key that is “password”, then it will remove that key from the data for the cookie.

# Implement this method if your account model
# does NOT have an email property on/in it.
def get_account_data_for_cookie(
    self,
    account: AccountOut
) -> Tuple[str, dict]:
    return account.username, account.dict()
Parameters

account_data (Union[BaseModel, dict]) – This will be whatever value is returned from get_account_data

Raises

BadAccountDataError – If the account_data cannot be converted into a dictionary.

Returns

  • sub (str) – This is the value for the “sub” claim of the JWT.

  • data (dict) – This is the data that will be encoded into the “account” claim of the JWT.

abstract get_account_getter(account_getter: Any) Any[source]#

Gets the thing that gets account data for your application

You MUST implement this method in your custom class!

This method can be used to resolve your account getter, or just for returning the function or object that you use to get your account data.

A typical implementation of this is just to use dependency injection for the thing you want from FastAPI, then return it. In the following code example, you have some class named AccountRepository that you use to get your account data. The implementation would look like this:

def get_account_getter(
    self,
    account_repo: AccountRepository = Depends(),
) -> AccountRepository:
    return account_repo
async get_current_account_data(account: dict = Depends(try_get_current_account_data)) dict[source]#

Get account data for a request

Like try_get_current_account_data, but raises an error if the account data cannot be found.

Use this method as a Depends when you want to protect and endpoint to only be accessible by an someone that’s got a JWT from logging in.

If the token does not exist, the method will raise a 401 error.

@router.post("/api/things")
async def create_thing(
    account_data: dict = Depends(authenticator.get_current_account_data),
):
    pass
Raises

HTTPException – If account data cannot be decoded from the JWT.

Returns

data – Returns the account data from the bearer token in the Authorization header or token. If the function can’t decode the token, then it returns None.

Return type

dict

get_exp(proposed: datetime.timedelta, account: Optional[Union[pydantic.main.BaseModel, dict]]) datetime.timedelta[source]#

Returns the amount of time before the JWT expires.

By default, returns the value passed into the exp parameter of the initializer.

This method was introduced in v0.3.0.

Returns

expiry – The interval for which the JWT should be valid from its point of creation.

Return type

timedelta

abstract get_hashed_password(account_data: Union[pydantic.main.BaseModel, dict]) Optional[str][source]#

Gets the hashed password from account data.

You MUST implement this method in your custom class!

Just return the hashed password from the data that you get from your data store for the account.

def get_hashed_password(self, account: Account):
    return account.hashed_password
Parameters

account_data (Union[BaseModel, dict]) – This will be whatever value is returned from get_account_data

Returns

hashed_password – This is the hashed password stored when creating an account (because you should not store passwords in the clear anywhere)

Return type

str

get_session_getter(session_getter: Optional[Any] = None) Any[source]#

Returns the object that handles session manipulation.

This method can be used to resolve your session getter, or just for returning the function or object that you use to get your session data.

A typical implementation of this is just to use dependency injection for the thing you want from FastAPI, then return it. In the following code example, you have some class named SessionRepository that you use to get your account data. The implementation would look like this:

def get_session_getter(
    self,
    session_repo: SessionRepository = Depends(),
) -> SessionRepository:
    return session_repo
Returns

session_getter – By default, this returns None

Return type

Any

hash_password(plain_password) str[source]#

Hashes a password for secure storage.

Use this method to hash your passwords so that they can later be verified by the authentication mechanism used by the Authenticator.

Use this method if you allow people to sign up for an account, for example. See the Quick Start.

async jti_created(jti: str, account: Union[pydantic.main.BaseModel, dict], session_getter: Any)[source]#

Handles when new JTIs are created.

Parameters
  • jti (str) – The new JWT identifier

  • account (Union[BaseModel, dict]) – The account information for which the JWT is being created

  • session_getter (Any) – The value returned from get_session_getter

async jti_destroyed(jti: str, session_getter: Any)[source]#

Handles when JTIs are destroyed.

Parameters
  • jti (str) – The JWT identifier that is being destroyed

  • session_getter (Any) – The value returned from get_session_getter

async login(response: starlette.responses.Response, request: starlette.requests.Request, form: fastapi.security.oauth2.OAuth2PasswordRequestForm = Depends(NoneType), account_getter=Depends(get_account_getter), session_getter=Depends(get_session_getter)) jwtdown_fastapi.authentication.Token[source]#

Authenticates credentials for an account.

If the data is correct, this creates a cookie set in the person’s browser that contains the JWT. It also returns a JSON payload that contains the JWT in a property named access_token.

Parameters
  • response (Response) – The response from the FastAPI call

  • request (Request) – The request from the FastAPI call

  • form (OAuth2PasswordRequestForm) – This can be an object that contains username and password attributes

  • account_getter (Any) – This is something to use to get your application’s account information

  • session_getter (Any) – This is something to use to get your application’s session information

Returns

token – An object with access_token and token_type attributes that contain the token information for use in AJAX calls

Return type

Token

async logout(request: starlette.requests.Request, response: starlette.responses.Response, session_getter=Depends(get_session_getter), jwt: Optional[dict] = None)[source]#

Logs a person out of their account.

This removes the cookie set in the person’s browser.

Parameters
  • request (Request) – The request from the FastAPI call

  • response (Response) – The response from the FastAPI call

property router#

Get a FastAPI router that has login and logout handlers.

Use this property to get a router to automatically register the login and logout path handlers for your application.

from authenticator import authenticator
from fastapi import APIRouter

app = FastAPI()
app.include_router(authenticator.router)
async try_get_current_account_data(bearer_token: Optional[str] = Depends(OAuth2PasswordBearer), cookie_token: Optional[str] = Cookie(None)) dict[source]#

Get account data for a request

This method will return the dictionary that is in the “account” claim of the JWT found in either the Authorization header or the cookie.

Use this method as a Depends when you want to get the current persons’s account information from their token.

If the token does not exist, you’ll get a None.

@router.get("/api/things")
async def get_things(
    account_data: Optional[dict] = Depends(authenticator.try_get_current_account_data),
):
    if account_data:
        return personalized_list
    return general_list
Returns

data – Returns the account data from the bearer token in the Authorization header or token. If the function can’t decode the token, then it returns None.

Return type

dict

async validate_jti(jti: str, session_getter: Any) bool[source]#

Validates that the jti is good.

By default, this returns True.

Parameters
  • jti (str) – The JWT identifier that is being destroyed

  • session_getter (Any) – The value returned from get_session_getter

Returns

is_valid – A value to indicate if the jti is valid.

Return type

bool

exception BadAccountDataError[source]#

Occurs when account data cannot be converted to a dictionary.

Raised when trying to convert account data into a subject claim and a dictionary of account data.

class Token(*, access_token: str, token_type: str = 'Bearer')[source]#

Represents a bearer token.

access_token: str#

Contains the encoded JWT

token_type: str#

This is set to “Bearer” because it’s a bearer token.