Wocha Docs

FastAPI quickstart

Add Wocha authentication to a Python FastAPI service with JWT access token validation on protected endpoints and the wocha Management SDK for admin operations.

Add Wocha authentication to a Python FastAPI service: validate JWT access tokens on protected endpoints and use the wocha Management SDK for admin operations.

Prerequisites

  • Python 3.10+
  • A Wocha project with an OAuth application (Wocha Console)

What you'll build

A FastAPI service with JWT-protected routes and Management API integration.

your-app.local

Welcome back

After sign-in

✓ Session stored securely

✓ User profile available in your app

✓ Organisation context ready for multi-tenant features

Installation

pip install fastapi uvicorn wocha PyJWT[crypto] httpx

Environment variables

Create a .env file:

WOCHA_ISSUER=https://your-tenant.auth.wocha.ai
WOCHA_AUDIENCE=your-api-audience          # optional
WOCHA_API_KEY=wocha_mgmt_...
WOCHA_TENANT=your-tenant-slug

JWT validation dependency

Create app/auth.py:

import os
from functools import lru_cache
from typing import Annotated
 
import httpx
import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
 
security = HTTPBearer()
 
 
@lru_cache
def get_jwks() -> dict:
    issuer = os.environ["WOCHA_ISSUER"].rstrip("/")
    response = httpx.get(f"{issuer}/.well-known/jwks.json", timeout=10)
    response.raise_for_status()
    return response.json()
 
 
def verify_token(token: str) -> dict:
    issuer = os.environ["WOCHA_ISSUER"].rstrip("/")
    audience = os.environ.get("WOCHA_AUDIENCE")
 
    jwks = get_jwks()
    header = jwt.get_unverified_header(token)
    kid = header.get("kid")
    key_data = next((k for k in jwks["keys"] if k.get("kid") == kid), None)
    if not key_data:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unknown signing key")
 
    public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key_data)
    options = {"verify_aud": audience is not None}
 
    return jwt.decode(
        token,
        public_key,
        algorithms=["RS256", "ES256"],
        issuer=issuer,
        audience=audience,
        options=options,
    )
 
 
async def get_current_user(
    credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
) -> dict:
    try:
        return verify_token(credentials.credentials)
    except jwt.PyJWTError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired token",
        ) from exc

Protected endpoint

Create app/main.py:

import os
from typing import Annotated
 
from fastapi import Depends, FastAPI
from wocha import WochaClient
 
from app.auth import get_current_user
 
app = FastAPI(title="Wocha FastAPI example")
 
wocha = WochaClient(
    tenant=os.environ["WOCHA_TENANT"],
    api_key=os.environ["WOCHA_API_KEY"],
)
 
 
@app.get("/health")
def health():
    return {"status": "ok"}
 
 
@app.get("/api/me")
def me(user: Annotated[dict, Depends(get_current_user)]):
    return {
        "user_id": user.get("sub"),
        "email": user.get("email"),
        "org_id": user.get("org_id"),
    }
 
 
@app.get("/api/admin/users")
def list_users(user: Annotated[dict, Depends(get_current_user)]):
    """Requires a valid user token; uses Management API key server-side."""
    result = wocha.users.list(page_size=10)
    return result

Run the server:

uvicorn app.main:app --reload --env-file .env

Testing the integration

  1. Start the API (command above).

  2. Obtain an access token from your frontend or the hosted login flow.

  3. Call the protected endpoint:

    curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" http://localhost:8000/api/me
  4. Open the interactive docs at http://localhost:8000/docs, click Authorize, and paste a bearer token to test endpoints in the browser.

Next steps

On this page