Skip to main content

FastAPI AI Kit ships a complete, production-grade authentication system: JWT tokens for user auth, API keys for programmatic access, and per-key rate limiting.

Overview

The kit implements two authentication methods:

| Method | Use case | Token lifetime | |--------|----------|----------------| | JWT (access token) | User login, dashboard sessions | 30 minutes | | JWT (refresh token) | Token renewal without re-login | 7 days | | API key | Programmatic SDK access | Until revoked |

JWT authentication

Registration and login

# POST /v1/auth/register
{
    "email": "dev@example.com",
    "password": "your-password"
}

# Response
{
    "access_token": "eyJ...",
    "refresh_token": "eyJ...",
    "token_type": "bearer"
}

Using the access token

Include the token in the Authorization header:

curl https://api.example.com/v1/profile \
    -H "Authorization: Bearer eyJ..."

Refreshing tokens

# POST /v1/auth/refresh
{
    "refresh_token": "eyJ..."
}

# Response
{
    "access_token": "eyJ...",
    "token_type": "bearer"
}

API keys

Creating a key

# POST /v1/keys (requires JWT auth)
{
    "name": "Production API Key",
    "tier": "pro"  # basic | pro | enterprise
}

# Response — raw key shown ONCE
{
    "id": "uuid",
    "key": "kit_live_abc123...",
    "prefix": "kit_live_ab",
    "name": "Production API Key",
    "tier": "pro"
}

Store the raw key securely — it's only shown once.

Using an API key

curl https://api.example.com/v1/chat \
    -H "X-API-Key: kit_live_abc123..."

Managing keys

# List keys (shows prefix, not full key)
GET /v1/keys

# Revoke a key
DELETE /v1/keys/{key_id}

Protecting routes

Use the provided decorators in your own route handlers:

from app.auth import require_api_key, rate_limit, get_current_user

# Require a valid API key
@router.post("/v1/your-endpoint")
async def endpoint(key: APIKey = Depends(get_api_key)):
    ...

# Require specific tier
@router.post("/v1/premium")
@require_api_key(tier=["pro", "enterprise"])
async def premium_endpoint(key: APIKey = Depends(get_api_key)):
    ...

# Require JWT auth (for user-facing routes)
@router.get("/v1/profile")
async def profile(user: User = Depends(get_current_user)):
    ...

Rate limiting

Rate limits are enforced per API key via Redis:

@router.post("/v1/chat")
@require_api_key(tier=["basic", "pro"])
@rate_limit(per_minute=60, per_day=5000)
async def chat(key: APIKey = Depends(get_api_key)):
    ...

Configure tier limits in app/config/tiers.py:

TIER_LIMITS = {
    "basic":      {"per_minute": 20,  "per_day": 1000},
    "pro":        {"per_minute": 60,  "per_day": 5000},
    "enterprise": {"per_minute": 300, "per_day": 50000},
}

Exceeding a limit returns HTTP 429 with Retry-After and X-RateLimit-Remaining headers.

Configuration

# .env
JWT_SECRET_KEY=generate-with-openssl-rand-hex-32
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_DAYS=7

Generate a secure secret:

openssl rand -hex 32