Authentication
Fastman provides one-command authentication scaffolding for the most common auth patterns. Each generates production-ready code with proper security practices built in.
| Type | Best For | Command |
|---|---|---|
| JWT | Stateless APIs, mobile apps | fastman install:auth --type=jwt |
| OAuth | Social login (Google, GitHub, Discord) | fastman install:auth --type=oauth --provider=google |
| Keycloak | Enterprise SSO | fastman install:auth --type=keycloak |
| Passkey | Passwordless (biometric/hardware key) | fastman install:auth --type=passkey |
JWT Authentication
The most common choice for stateless API authentication. Tokens are signed, self-contained, and require no server-side session storage.
fastman install:auth --type=jwt
This creates a complete auth feature module:
app/features/auth/
├── __init__.py
├── models.py # User model with password hashing
├── schemas.py # Login, Register, Token schemas
├── security.py # JWT token creation/verification
├── service.py # Auth business logic
├── dependencies.py # get_current_user dependency
└── router.py # /register, /login, /me endpoints
Generated Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/register | Create new user |
| POST | /auth/login | Get access token |
| GET | /auth/me | Get current user |
Usage Example
from app.features.auth.dependencies import get_current_user
from app.features.auth.models import User
@router.get("/protected")
def protected_route(user: User = Depends(get_current_user)):
return {"message": f"Hello, {user.email}"}
Configuration
Auth variables are automatically added to all environment files (.env.*):
SECRET_KEY=your-secret-key-here
ACCESS_TOKEN_EXPIRE_MINUTES=30
Generate a secure secret key (updates all env files):
fastman config:appkey
OAuth Authentication
For "Login with Google/GitHub/etc." social login flows. Fully scaffolded with provider-specific configuration.
# Google (default)
fastman install:auth --type=oauth --provider=google
# GitHub
fastman install:auth --type=oauth --provider=github
# Discord
fastman install:auth --type=oauth --provider=discord
Supported Providers
| Provider | Scopes | What You Get |
|---|---|---|
openid email profile | Email, name, avatar | |
| GitHub | read:user user:email | Email, name, avatar |
| Discord | identify email | Email, username, avatar |
Generated Files
app/features/auth/
├── oauth_config.py # Provider configuration (authlib)
├── models.py # User model with oauth_provider field
├── schemas.py # UserRead, OAuthCallback schemas
├── service.py # Create-or-update user from OAuth data
└── router.py # /login, /callback, /me, /logout
Generated Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /auth/login | Redirect to OAuth provider |
| GET | /auth/callback | Handle provider callback |
| GET | /auth/me | Get current user |
| GET | /auth/logout | Clear session |
Configuration
OAUTH_CLIENT_ID=your-client-id
OAUTH_CLIENT_SECRET=your-client-secret
Add SessionMiddleware to your app/main.py for OAuth session support:
from starlette.middleware.sessions import SessionMiddleware
app.add_middleware(SessionMiddleware, secret_key=settings.SECRET_KEY)
Keycloak Authentication
Enterprise-grade identity and access management with single sign-on (SSO). Powered by fastapi-keycloak.
fastman install:auth --type=keycloak
If your Keycloak instance or upstream gateway uses a private CA, append your project certificates during setup:
fastman install:auth --type=keycloak --append-certificate
This:
- Installs
fastapi-keycloakandcertifi - Creates
app/core/keycloak.pywith lazy-initializedFastAPIKeycloakinstance, Swagger OAuth config, and aGET /meendpoint - Updates
app/core/config.pywith Keycloak settings - Adds environment variables to all
.env.*files - Automatically uses certificates from
certs/when present by building a merged CA bundle for the running app
The FastAPIKeycloak instance is not created at import time. It is initialized inside init_keycloak(app) during application startup. This gives you a safer startup flow:
- Custom certificates are loaded before any HTTPS call to Keycloak
- No network calls happen during module import
idpandget_current_userare populated only afterinit_keycloak()runs- If Keycloak is temporarily unreachable, the app still starts and logs that Keycloak is disabled
- If Keycloak returns
unauthorized_client, the app still starts with admin features disabled
Generated Endpoint
| Method | Endpoint | Description |
|---|---|---|
| GET | /me | Return the current authenticated Keycloak user |
Configuration
KEYCLOAK_URL=https://keycloak.example.com
KEYCLOAK_REALM=my-realm
KEYCLOAK_CLIENT_ID=my-client
KEYCLOAK_CLIENT_SECRET=your-secret
KEYCLOAK_ADMIN_SECRET=
KEYCLOAK_CALLBACK_URI=http://localhost:8000/callback
KEYCLOAK_VERIFY_SSL=true
| Variable | Description | Default |
|---|---|---|
KEYCLOAK_URL | Keycloak server URL (with /auth suffix if required by your version) | http://localhost:8080 |
KEYCLOAK_REALM | Keycloak realm name | master |
KEYCLOAK_CLIENT_ID | Client ID for your application | — |
KEYCLOAK_CLIENT_SECRET | Client secret | — |
KEYCLOAK_ADMIN_SECRET | Secret for the admin-cli client. Only needed if you want admin operations such as managing users, roles, or groups from code. | empty |
KEYCLOAK_CALLBACK_URI | OAuth2 callback URL — must match a Valid Redirect URI in Keycloak | http://localhost:8000/callback |
KEYCLOAK_VERIFY_SSL | true — enable SSL verification; false — disable (not recommended for production) | true |
Minimal vs Admin-Enabled Setup
For most projects, you only need these values:
KEYCLOAK_URLKEYCLOAK_REALMKEYCLOAK_CLIENT_IDKEYCLOAK_CLIENT_SECRETKEYCLOAK_CALLBACK_URI
That is enough for:
Depends(get_current_user)route protection- the generated
GET /meendpoint - Swagger's Authorize button
KEYCLOAK_ADMIN_SECRET is only needed when you want to call admin methods on idp, such as creating users or assigning roles. Fastman does not expose a KEYCLOAK_ADMIN_CLIENT_ID setting; fastapi-keycloak uses its default admin client (admin-cli) internally.
Protecting Routes
Unlike middleware-based approaches, fastapi-keycloak uses dependency injection. Routes are unprotected by default — you opt in per-route:
from fastapi import Depends
from fastapi_keycloak import OIDCUser
from app.core.keycloak import get_current_user
@router.get("/protected")
def protected_route(user: OIDCUser = Depends(get_current_user)):
return {"message": f"Hello, {user.email}"}
To require specific roles:
from app.core.keycloak import idp
get_admin = idp.get_current_user(required_roles=["admin"])
@router.get("/admin")
def admin_route(user: OIDCUser = Depends(get_admin)):
return {"message": f"Welcome admin {user.email}"}
Swagger Authorize Button
init_keycloak(app) calls idp.add_swagger_config(app) and registers the /me endpoint. The Swagger UI automatically gets an Authorize button — click it to authenticate with Keycloak before testing protected endpoints.
If Keycloak cannot be reached at startup, the app still boots and your non-Keycloak routes continue to work. In that state, /me and any route protected with get_current_user remain unavailable until Keycloak becomes reachable again.
Private CA / Certificate Support
For environments that terminate TLS with an internal CA (e.g. corporate proxies, on-prem Keycloak), store your certificate chain in the project-level certs/ directory using .pem or .crt files.
your-project/
├── app/
├── certs/
│ └── company-root-chain.pem
└── pyproject.toml
Certificates are loaded automatically at startup. When init_keycloak(app) runs, it scans certs/ for .pem and .crt files, builds a merged CA bundle from certifi plus your project certificates, and points requests and Python SSL at that merged bundle before creating the FastAPIKeycloak instance. No separate script or manual step is needed — just drop your certs and start the server.
You can also set CERTS_PATH environment variable to point to a custom certificates directory instead of certs/.
Alternatively, you can prepare the certificate bundle independently:
# Prepare during Keycloak installation
fastman install:auth --type=keycloak --append-certificate
# Or as a standalone step
fastman install:cert
This approach is non-destructive: the installed certifi bundle is left untouched, and the generated merged bundle is rebuilt when the app starts. fastman install:cert is optional for the generated Keycloak scaffold, but useful when you want to prebuild the bundle, validate your certificate files, or expose the paths through env files for other tools and scripts.
Passkey Authentication (WebAuthn)
Passwordless authentication using biometrics, hardware keys, or device PINs. No passwords, no MFA — just tap your fingerprint or security key.
fastman install:auth --type=passkey
How It Works
- Registration: User's device creates a public/private key pair. The public key is stored on your server.
- Authentication: The server sends a challenge, the device signs it with the private key, and the server verifies using the stored public key.
No password is ever created, stored, or transmitted.
Generated Files
app/features/auth/
├── models.py # User + PasskeyCredential models
├── schemas.py # Registration/Authentication request schemas
├── service.py # WebAuthn challenge generation & verification
├── security.py # JWT session tokens (post-auth)
├── dependencies.py # get_current_user dependency
└── router.py # Registration & authentication endpoints
Generated Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /auth/passkey/register/options | Get registration challenge |
| POST | /auth/passkey/register/verify | Store new passkey |
| POST | /auth/passkey/authenticate/options | Get login challenge |
| POST | /auth/passkey/authenticate/verify | Verify & get token |
| GET | /auth/passkey/me | Current user info |
| GET | /auth/passkey/credentials | List registered passkeys |
| DELETE | /auth/passkey/credentials/{id} | Remove a passkey |
Configuration
PASSKEY_RP_ID=localhost
PASSKEY_RP_NAME=My App
PASSKEY_ORIGIN=http://localhost:8000
In production, set PASSKEY_RP_ID to your domain (e.g. example.com) and PASSKEY_ORIGIN to https://example.com.
Frontend Integration
The backend provides the WebAuthn options and verification. You'll need a small JavaScript client to interact with the browser's navigator.credentials API:
// Registration
const options = await fetch('/auth/passkey/register/options', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'user@example.com', username: 'user' })
}).then(r => r.json());
const credential = await navigator.credentials.create({ publicKey: options });
await fetch('/auth/passkey/register/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'user@example.com', credential })
});
Passkeys eliminate passwords entirely. No password reuse, no phishing, no MFA fatigue. A single biometric scan or hardware tap replaces passwords + SMS codes + authenticator apps.
Best Practices
- Always use HTTPS in production
- Store secrets in environment variables, never in code
- Use short-lived tokens (15-30 minutes for JWT, 60 minutes for passkey sessions)
- Implement refresh tokens for long sessions
- Hash passwords with bcrypt (Fastman does this automatically for JWT)
- Prefer passkeys for new projects — they're more secure and easier for users