FastAPI integration — dependency reference

This page documents the main FastAPI helpers exported by authtuna.integrations.fastapi_integration. Each entry shows the purpose, available options, and copyable examples (cookie sessions and bearer/API-key flows).

Quick import

from fastapi import FastAPI, Depends, Request

from authtuna.integrations.fastapi_integration import (
    get_current_user,
    get_current_user_optional,
    get_user_ip,
    resolve_token_method,
    PermissionChecker,
    RoleChecker,
)

app = FastAPI()

Dependency: get_current_user(request, allow_public_key=False)

Returns the authenticated User instance. Supports two authentication methods: - COOKIE (session middleware must populate request.state.user_id) - BEARER (Authorization: Bearer <api_key>)

Options

  • allow_public_key (bool, default False) — if True, publishable API keys (prefix API_KEY_PREFIX_PUBLISHABLE) are allowed; otherwise the function rejects them with 403.

Examples

# Cookie-backed session (requires session middleware)
@app.get('/dashboard')
async def dashboard(user = Depends(get_current_user)):
    return {"welcome": f"Hello {user.email}"}

# Allow publishable keys for a public endpoint (use carefully)
@app.get('/public-data')
async def public_data(user = Depends(lambda req: get_current_user(req, allow_public_key=True))):
    return {"ok": True}

Note: the second example shows using a small wrapper to pass the option — you can create a reusable dependency factory in your app for that pattern.

Dependency: get_current_user_optional(request)

Same behavior as get_current_user but returns None when the request is unauthenticated instead of raising an HTTP error. Useful for endpoints that accept both anonymous and authenticated visitors.

@app.get('/home')
async def home(user = Depends(get_current_user_optional)):
    if user:
        return {"message": f"Welcome back {user.email}"}
    return {"message": "Welcome, please sign in"}

Dependency: get_user_ip(request)

Returns the resolved IP address (populated by the session middleware). Use this for logging, rate limiting, or region-locking decisions.

@app.get('/whoami')
async def whoami(ip: str = Depends(get_user_ip)):
    return {"ip": ip}

Utility: resolve_token_method(request)

Returns the token method inferred for the request: "COOKIE", "BEARER", or None. It reads request.state.token_method if set by middleware; otherwise you can set it manually during tests.

def handler(request: Request):
    method = resolve_token_method(request)
    if method == 'COOKIE':
        # UI session
        pass
    elif method == 'BEARER':
        # API key
        pass
    else:
        # unauthenticated
        pass

PermissionChecker — options & examples

A dependency factory that enforces permission checks. It supports both COOKIE sessions and BEARER API keys and handles master keys vs scoped keys differently.

Constructor options

ParameterTypeDefaultDescription
*permissionsstr...Permission strings to check (e.g. 'projects.read')
mode'AND' | 'OR''AND'AND => all permissions required; OR => any one required
scope_prefixOptional[str]NoneIf set, used as the prefix when deriving a scope from a path parameter
scope_from_pathOptional[str]NoneName of the path parameter to derive scope from (e.g. 'project_id')
raise_errorboolTrueWhen False the dependency returns None on failure instead of raising

Examples

# Require a specific permission scoped to path param project_id
@app.get('/projects/{project_id}')
async def read_project(project_id: str, user = Depends(PermissionChecker('projects.read', scope_from_path='project_id'))):
    return {"project_id": project_id}

# Require any of several permissions (OR mode)
@app.post('/projects/{project_id}/action')
async def project_action(project_id: str, user = Depends(PermissionChecker('projects.write', 'projects.admin', mode='OR', scope_from_path='project_id'))):
    return {"ok": True}

Behavior notes: for BEARER keys, master keys are treated like cookie sessions and evaluate the user's current roles/permissions dynamically; scoped keys are checked against the API key's granted scopes.

RoleChecker — options & examples

ParameterTypeDefaultDescription
*rolesstr...Role names to require (e.g. 'admin')
mode'AND' | 'OR''AND'AND => all roles required; OR => any one required
scope_prefixOptional[str]NoneSame behavior as PermissionChecker
scope_from_pathOptional[str]NoneDerive the scope from the named path parameter
raise_errorboolTrueWhen False the dependency returns None instead of raising

Examples

# Require admin role
@app.post('/admin/only')
async def admin_endpoint(user = Depends(RoleChecker('admin'))):
    return {"ok": True}

# Require either manager OR admin (OR mode)
@app.post('/manage/{org_id}')
async def manage(org_id: str, user = Depends(RoleChecker('manager', 'admin', mode='OR', scope_from_path='org_id'))):
    return {"ok": True}

Testing & tips

  • When unit-testing, you can set request.state.token_method, request.state.user_id, and request.state.user_object manually to emulate middleware behavior.
  • If your app is API-only, you can skip middleware and rely on BEARER API keys for authentication and permission checks.
  • To allow publishable keys on an endpoint (careful), call get_current_user with allow_public_key=True via a small wrapper dependency.
  • Use raise_error=False when you prefer returning None and handling authorization failures inside the route (for custom responses).

For implementation details see authtuna/integrations/fastapi_integration.py in the source tree.