Skip to content

auth

A minimal public-API module. It owns the import paths every other module uses to read the current user (UserContext, get_current_user, require_permission) — but ships no routes, no models, no UI. The actual login/session/middleware logic lives in users.

The split exists so you can swap out the users module (e.g. to plug in OAuth or LDAP) without churning every consumer's import paths.

ModuleMeta

FieldValue
nameAuth
route_prefix/auth
view_prefix(none)
depends_on(none)

Public contracts

python
from auth.contracts.schemas import UserContext

UserContext — frozen-ish dataclass populated by the AuthMiddleware (in users). Fields:

FieldTypeNotes
idUUID | NoneNone for anonymous requests
emailstrempty string when anonymous
full_namestr
roleslist[str]role names, e.g. ["admin"]
tenant_idstr | Noneonly set when SM_MULTI_TENANT=true
is_authenticatedboolproperty — id is not None

It also exposes to_session_dict() / from_session_dict() for the middleware to (de)serialise itself into the session cookie.

Dependencies for endpoints

python
from auth.deps import CurrentUser, get_current_user, require_permission
DependencyWhat it does
CurrentUser (Annotated alias)FastAPI dependency that returns the active UserContext. Raises 401 if request.state.user isn't set.
get_current_user(request, t)The underlying callable. Use this when you need to compose it manually.
require_permission("orders.view", "orders.edit")Returns a dependency that 403s if the user is missing any of the listed permissions. The admin role bypasses the check.

Example: protecting a route

python
from fastapi import APIRouter, Depends
from auth.deps import CurrentUser, require_permission

router = APIRouter()

@router.get("/me")
async def me(user: CurrentUser) -> dict:
    return {"email": user.email, "roles": user.roles}

@router.delete(
    "/orders/{order_id}",
    dependencies=[Depends(require_permission("orders.delete"))],
)
async def delete_order(order_id: int, user: CurrentUser) -> None:
    # `user` is guaranteed authenticated here
    ...

For permission checks that need to honour direct user grants (not just role-derived perms), use RequiresPermission from the permissions module instead.

Locales

KeyDefault
errors.not_authenticated"Not authenticated"
errors.missing_permission"Missing required permission: {permissions}"

Translated automatically when the request locale is set — no extra wiring needed.

Released under the MIT License.