Skip to content

Auth Manager

The BaseAuthManager is responsible for managing authentication, authorization, and ORM models management. This is layer between client and Database. It uses Repositories to interact with the database.

Creation

To create AuthManager, simply inherit BaseAuthManager from fastauth.manager module. After inhering we need to override parse_id(self, pk: str) method. This method is used to convert user primary key from string(which we get from client or token) to the type stored in ORM model. We can pass ORM models to the BaseAuthManager Generic for type hinting.

import uuid
from fastauth.manager import BaseAuthManager
from fastapi import Request


class AuthManager(BaseAuthManager[User, uuid.UUID, Role, Permission, OAuthAccount]):

    def parse_id(self, pk: str):
        return uuid.UUID(pk)

Also we use AuthManager to override event methods like on_after_login, on_after_register etc. Supported events are:

import uuid
from fastauth.manager import BaseAuthManager
from fastapi import Request


class AuthManager(BaseAuthManager[User, uuid.UUID, Role, Permission, OAuthAccount]):

    def parse_id(self, pk: str):
        return uuid.UUID(pk)

    async def on_after_login(self, user, request: Request | None = None):
        print(f"User {user.email} logged in")

    async def on_after_register(self, user, request: Request | None = None):
        print(f"User {user.email} registered")

Supported events

  • on_after_login - called when user logged in. Provide User model and Request

Dependency

We can define dependency callable for AuthManager, to passed it in FastAuth class. The main differeces from other dependencies callable, is than callable take FastAuthConfig instance as first argument. So if we use SQLAlchemy ORM, we need to create Repositories which need AsyncSession dependency, and pass it to AuthManager:

async def get_auth_manager(
    config: FastAuthConfig,
    session: AsyncSession = Depends(get_db),
):
    return AuthManager(
        config,
        UserRepository(session),
        RBACRepository(session),
        OAuthRepository(session),
    )

Auth manager also get password helper and token encoder instances, which can be passed to __init__ method.

async def get_auth_manager(
    config: FastAuthConfig,
    session: AsyncSession = Depends(get_db),
):
    return AuthManager(
        config,
        UserRepository(session),
        RBACRepository(session),
        OAuthRepository(session),
        password_helper=PasswordHelper(),
    )

Password Helper

Password helper is used to hash and verify passwords. It should implement PasswordHelperProtocol from fastauth.utils.password module. By default BaseAuthManager uses PasswordHelper class, which uses argon2 and bcrypt hashing algorithms, provided by pwdlib package

class PasswordHelperProtocol(Protocol):
    def verify_and_update(
        self, plain_password: str, hashed_password: str
    ) -> tuple[bool, str | None]: ...  # pragma: no cover

    def hash(self, password: str) -> str: ...  # pragma: no cover

    def generate(self) -> str: ...  # pragma: no cover

PasswordHelper

class PasswordHelper(PasswordHelperProtocol):
    def __init__(self, password_hash: PasswordHash | None = None) -> None:
        if password_hash is None:
            self.password_hash = PasswordHash(
                (
                    Argon2Hasher(),
                    BcryptHasher(),
                )
            )
        else:
            self.password_hash = password_hash  # pragma: no cover

    def verify_and_update(
        self, plain_password: str, hashed_password: str
    ) -> tuple[bool, str | None]:
        return self.password_hash.verify_and_update(plain_password, hashed_password)

    def hash(self, password: str) -> str:
        return self.password_hash.hash(password)

    def generate(self) -> str:
        return secrets.token_urlsafe()

Token Encoder

Token encoder is used to encode and decode tokens, as well as TokenStrategy but on lower level. It should implement TokenEncoderProtocol from fastauth.utils.jwt_helper module. By default it use JWT class from fastauth.utils.jwt_helper module.

class TokenHelperProtocol(Protocol):
    def decode_token(self, token: str, *args, **kwargs) -> dict[str, Any]:
        raise NotImplementedError

    def encode_token(
        self, payload: dict[str, Any], token_type: TokenType, *args, **kwargs
    ) -> str:
        raise NotImplementedError

JWT helper class

class JWTHelper:
    def __init__(self, secretkey: str, algorithm: str):
        self._secretkey = secretkey
        self._algorithm = algorithm

    def decode_token(
        self, token: str, audience: StringOrSequence | None = None, **kwargs
    ):
        return jwt.decode(
            token,
            key=self._secretkey,
            algorithms=[self._algorithm],
            audience=audience,
            **kwargs,
        )

    def encode_token(
        self,
        payload: dict[str, Any],
        token_type: TokenType,
        max_age: int | None = None,
        audience: StringOrSequence | None = None,
        headers: dict[str, Any] | None = None,
        **kwargs,
    ):
        payload["type"] = payload.get("type", token_type)
        payload["aud"] = payload.get("aud", audience)
        payload["iat"] = payload.get("iat", datetime.now(timezone.utc))
        payload["exp"] = payload.get(
            "exp", payload.get("iat") + timedelta(seconds=max_age)
        )
        return jwt.encode(
            payload,
            key=self._secretkey,
            algorithm=self._algorithm,
            headers=headers,
            **kwargs,
        )