HelloJohn / docs
SDKsPython SDK

FastAPI

Integrate HelloJohn authentication into FastAPI — dependency injection, route protection, and role-based access control.

FastAPI

Use HelloJohn with FastAPI's dependency injection system for clean, typed authentication.


Installation

pip install hellojohn fastapi

Setup

import os
from hellojohn import HelloJohn

hj = HelloJohn(
    tenant_id=os.environ["HELLOJOHN_TENANT_ID"],
    secret_key=os.environ["HELLOJOHN_SECRET_KEY"],
)

Auth Dependency

Create a reusable FastAPI dependency:

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from hellojohn import InvalidTokenError, TokenPayload

security = HTTPBearer()

async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
) -> TokenPayload:
    try:
        return hj.verify_token(credentials.credentials)
    except InvalidTokenError as e:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(e),
            headers={"WWW-Authenticate": "Bearer"},
        )

Protected Routes

from fastapi import FastAPI

app = FastAPI()

@app.get("/api/me")
async def get_me(auth: TokenPayload = Depends(get_current_user)):
    return {"userId": auth.sub, "email": auth.email}

@app.post("/api/posts")
async def create_post(
    post: PostCreate,
    auth: TokenPayload = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    return db.create_post(author_id=auth.sub, **post.model_dump())

Role-Based Access

from functools import partial

def require_role(role: str):
    async def check_role(auth: TokenPayload = Depends(get_current_user)):
        if auth.org_role not in (role, "owner"):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Insufficient permissions",
            )
        return auth
    return check_role

# Admin-only endpoint
@app.delete("/api/users/{user_id}")
async def delete_user(
    user_id: str,
    auth: TokenPayload = Depends(require_role("admin")),
):
    await hj.users.delete(user_id)
    return {"deleted": user_id}

Optional Auth

Allow both authenticated and anonymous access:

from fastapi.security import HTTPBearer

optional_bearer = HTTPBearer(auto_error=False)

async def get_optional_user(
    credentials: HTTPAuthorizationCredentials | None = Depends(optional_bearer),
) -> TokenPayload | None:
    if not credentials:
        return None
    try:
        return hj.verify_token(credentials.credentials)
    except InvalidTokenError:
        return None

@app.get("/api/content")
async def get_content(auth: TokenPayload | None = Depends(get_optional_user)):
    if auth:
        return get_personalized_content(auth.sub)
    return get_public_content()

Full Application Example

from fastapi import FastAPI, Depends, HTTPException, status
from contextlib import asynccontextmanager
from hellojohn import HelloJohn

hj = HelloJohn(
    tenant_id=os.environ["HELLOJOHN_TENANT_ID"],
    secret_key=os.environ["HELLOJOHN_SECRET_KEY"],
)

app = FastAPI(title="My API")

# Auth dependency
async def auth(credentials = Depends(HTTPBearer())):
    try:
        return hj.verify_token(credentials.credentials)
    except Exception:
        raise HTTPException(status_code=401, detail="Unauthorized")

# Routes
@app.get("/health")
def health():
    return {"ok": True}

@app.get("/api/me", dependencies=[Depends(auth)])
async def me(payload = Depends(auth)):
    user = await hj.users.get_async(payload.sub)
    return user

On this page