Skip to content

Dependencies

All dependencies live in fastapi_simple_tinyauth.helpers and are meant to be used with FastAPI's Depends. They read the AuthUser that the middleware stored on request.state.auth_user.

optional_user

Returns the AuthUser when authenticated, or None when the request is anonymous.

from typing import Annotated

from fastapi import Depends
from fastapi_simple_tinyauth.helpers import optional_user
from fastapi_simple_tinyauth.models import AuthUser


@app.get("/")
async def index(user: Annotated[AuthUser | None, Depends(optional_user)] = None):
    return {"authenticated": user is not None}

require_user

Returns the AuthUser, or raises UnauthenticatedError (401) when anonymous.

from typing import Annotated

from fastapi import Depends
from fastapi_simple_tinyauth.helpers import require_user
from fastapi_simple_tinyauth.models import AuthUser


@app.get("/me")
async def me(user: Annotated[AuthUser, Depends(require_user)]):
    return {"username": user.username}

Group dependencies

The three group helpers are factories: you call them with the group requirement and they return a dependency callable. Each returns the AuthUser on success, raises UnauthenticatedError (401) when anonymous, and ForbiddenError (403) when the group requirement is not met.

require_group

The user must belong to a single group.

from typing import Annotated

from fastapi import Depends
from fastapi_simple_tinyauth.helpers import require_group
from fastapi_simple_tinyauth.models import AuthUser


@app.get("/admin")
async def admin(user: Annotated[AuthUser, Depends(require_group("admins"))]):
    return {"username": user.username}

require_all_groups

The user must belong to every listed group. An empty list imposes no requirement (any authenticated user passes).

from typing import Annotated

from fastapi import Depends
from fastapi_simple_tinyauth.helpers import require_all_groups
from fastapi_simple_tinyauth.models import AuthUser


@app.get("/release")
async def release(user: Annotated[AuthUser, Depends(require_all_groups(["staff", "maintainers"]))]):
    return {"username": user.username}

require_any_group

The user must belong to at least one listed group. An empty list can never be satisfied and always raises ForbiddenError.

from typing import Annotated

from fastapi import Depends
from fastapi_simple_tinyauth.helpers import require_any_group
from fastapi_simple_tinyauth.models import AuthUser


@app.get("/staff")
async def staff(user: Annotated[AuthUser, Depends(require_any_group(["admins", "staff"]))]):
    return {"username": user.username}

Route-level enforcement

You can also apply a group dependency without consuming the user in the handler, using the route's dependencies list:

from fastapi import Depends
from fastapi_simple_tinyauth.helpers import require_group


@app.get("/admin", dependencies=[Depends(require_group("admins"))])
async def admin_area():
    return {"ok": True}

The AuthUser object

AuthUser is a frozen dataclass. username is required (an AuthUser always represents a logged-in user); the rest are optional: email, name, groups (a tuple[str, ...]), and sub. It also offers:

  • has_group(group) — membership in one group.
  • has_all_groups(groups) — membership in all listed groups.
  • has_any_group(groups) — membership in at least one listed group.

Using the helpers without FastAPI

Depends is the only FastAPI-specific piece here; the helpers themselves are plain functions that take a Starlette Request and return an AuthUser (or raise). In a plain Starlette app you call them directly inside your handler:

from starlette.requests import Request
from starlette.responses import JSONResponse

from fastapi_simple_tinyauth.helpers import require_group, require_user


async def me(request: Request) -> JSONResponse:
    user = require_user(request)              # no Depends
    return JSONResponse({"username": user.username})


async def admin(request: Request) -> JSONResponse:
    user = require_group("admins")(request)   # the factory returns a callable
    return JSONResponse({"username": user.username})

The raised exceptions subclass Starlette's HTTPException, so Starlette renders them without any extra wiring. See Middleware for how to add the middleware to a Starlette app.