13 KiB
Security API Documentation
API documentation for security-related endpoints and how to use security components in routes.
Table of Contents
Overview
The Security component provides authentication endpoints and utilities that routes can use to protect their endpoints and access user context. This document focuses on the API surface and how to use security components from route handlers.
For detailed information about the Security component architecture and internal workings, see Security Component Documentation.
Authentication Endpoints
Local Authentication
Base Path: /api/local
POST /api/local/login
Authenticate with username and password.
Request:
- Content-Type:
application/x-www-form-urlencoded - Headers:
X-CSRF-Token(required) - Body:
username,password(form data)
Response:
- Status:
200 OK - Cookies:
auth_token(httpOnly),refresh_token(httpOnly) - Body:
{
"type": "local_auth_success",
"message": "Login successful - tokens set in httpOnly cookies",
"authenticationAuthority": "local",
"expires_at": "2024-01-01T12:00:00"
}
Rate Limit: 30 requests per minute
POST /api/local/register
Register a new user account.
Request:
- Content-Type:
application/json - Body: User object + password
Response: User object
Rate Limit: 10 requests per minute
GET /api/local/me
Get current authenticated user information.
Request:
- Headers:
Authorization: Bearer <token>OR Cookie:auth_token
Response: User object
Rate Limit: 30 requests per minute
POST /api/local/refresh
Refresh access token using refresh token.
Request:
- Cookie:
refresh_token(httpOnly)
Response:
- Cookies: New
auth_tokenandrefresh_token - Body: Token refresh response
Rate Limit: 60 requests per minute
POST /api/local/logout
Logout current user and invalidate tokens.
Request:
- Headers:
Authorization: Bearer <token>OR Cookie:auth_token
Response: Logout confirmation
Rate Limit: 10 requests per minute
Microsoft OAuth Authentication
Base Path: /api/msft
GET /api/msft/login
Initiate Microsoft OAuth login flow.
Response: Redirects to Microsoft OAuth login page
GET /api/msft/auth/callback
OAuth callback endpoint (handled by Microsoft).
Query Parameters:
code: Authorization code from Microsoftstate: State parameter for CSRF protection
Response: HTML page that sets cookies and redirects
GET /api/msft/me
Get current authenticated user information.
Request:
- Headers:
Authorization: Bearer <token>OR Cookie:auth_token
Response: User object
POST /api/msft/logout
Logout current user.
Request:
- Headers:
Authorization: Bearer <token>OR Cookie:auth_token
Response: Logout confirmation
Google OAuth Authentication
Base Path: /api/google
GET /api/google/login
Initiate Google OAuth login flow.
Query Parameters:
state: Optional state parameter (default: "login")connectionId: Optional connection ID for connection flow
Response: Redirects to Google OAuth login page
GET /api/google/auth/callback
OAuth callback endpoint (handled by Google).
Query Parameters:
code: Authorization code from Googlestate: State parameter for CSRF protection
Response: HTML page that sets cookies and redirects
GET /api/google/me
Get current authenticated user information.
Request:
- Headers:
Authorization: Bearer <token>OR Cookie:auth_token
Response: User object
POST /api/google/logout
Logout current user.
Request:
- Headers:
Authorization: Bearer <token>OR Cookie:auth_token
Response: Logout confirmation
Authentication Flows
Login Flow (Local Authentication)
sequenceDiagram
participant Client
participant Route as Login Route<br/>POST /api/local/login
participant Auth as auth.py
participant JWT as jwtService.py
participant Interface as Interface Layer
participant DB as Database
Client->>Route: POST /api/local/login<br/>(username, password, X-CSRF-Token)
Route->>Route: Validate CSRF Token
Route->>Interface: authenticateLocalUser(username, password)
Interface->>DB: Query User & Verify Password
DB-->>Interface: User Record
alt Invalid Credentials
Interface-->>Route: None
Route-->>Client: HTTPException 401
end
Route->>JWT: createAccessToken(userData)
JWT->>JWT: Generate JTI (UUID)
JWT->>JWT: Set Expiration
JWT->>JWT: Sign JWT
JWT-->>Route: Access Token + Expires At
Route->>JWT: createRefreshToken(userData)
JWT-->>Route: Refresh Token + Expires At
Route->>Interface: Save Token to DB<br/>(for LOCAL authority)
Interface->>DB: Insert Token Record
Route->>JWT: setAccessTokenCookie(response, token)
JWT->>JWT: Set httpOnly Cookie<br/>(secure, samesite=strict)
Route->>JWT: setRefreshTokenCookie(response, token)
JWT->>JWT: Set httpOnly Cookie
Route-->>Client: HTTP Response<br/>(with Cookies)
OAuth Flow (Microsoft/Google)
sequenceDiagram
participant Client
participant Route as OAuth Route<br/>GET /api/{provider}/login
participant Provider as OAuth Provider<br/>(MSFT/Google)
participant JWT as jwtService.py
participant Interface as Interface Layer
participant DB as Database
Client->>Route: GET /api/{provider}/login
Route->>Route: Generate OAuth State
Route->>Provider: Redirect to OAuth URL<br/>(with state, client_id, redirect_uri)
Provider-->>Client: OAuth Login Page
Client->>Provider: User Authenticates
Provider->>Route: GET /api/{provider}/callback<br/>(code, state)
Route->>Route: Validate State
Route->>Provider: Exchange Code for Tokens<br/>(POST /token)
Provider-->>Route: Access Token + Refresh Token
Route->>Provider: Get User Info<br/>(using Access Token)
Provider-->>Route: User Profile Data
Route->>Interface: Find/Create User<br/>(by external ID)
Interface->>DB: Query/Create User
DB-->>Interface: User Record
Interface-->>Route: User Object
Route->>Interface: Save/Create Connection
Interface->>DB: Save Connection Record
Route->>Interface: Save Token
Interface->>DB: Save Token Record
Route->>JWT: createAccessToken(userData)
JWT-->>Route: Gateway JWT Token
Route->>JWT: setAccessTokenCookie(response, token)
Route->>JWT: setRefreshTokenCookie(response, token)
Route-->>Client: HTTP Response<br/>(with Gateway Cookies)
Using Security in Routes
Basic Authentication Pattern
All protected routes use the getCurrentUser dependency to access the authenticated user:
from fastapi import APIRouter, Depends
from modules.security.auth import getCurrentUser
from modules.datamodels.datamodelUam import User
router = APIRouter(prefix="/api/example", tags=["Example"])
@router.get("/protected")
async def protected_endpoint(
currentUser: User = Depends(getCurrentUser)
) -> dict:
"""
Protected endpoint that requires authentication.
The currentUser parameter is automatically populated by getCurrentUser.
"""
return {
"message": f"Hello, {currentUser.username}!",
"userId": currentUser.id,
"mandateId": currentUser.mandateId
}
Rate Limiting
Use the limiter from the security module to add rate limiting:
from modules.security.auth import getCurrentUser, limiter
from fastapi import Request
@router.post("/action")
@limiter.limit("30/minute")
async def rate_limited_endpoint(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> dict:
"""Endpoint with rate limiting (30 requests per minute)"""
return {"status": "success"}
Cookie vs Header Authentication
The security component supports both authentication methods:
-
Cookie-based (preferred for web apps):
- Tokens are automatically sent via httpOnly cookies
- More secure (not accessible via JavaScript)
- Automatically included in requests from same origin
-
Header-based (for API clients):
- Use
Authorization: Bearer <token>header - Required for programmatic API access
- Tokens can be obtained from login endpoints
- Use
The CookieAuth class checks cookies first, then falls back to headers.
Creating Tokens in Routes
When implementing custom authentication endpoints, use the JWT service:
from modules.security.jwtService import (
createAccessToken,
createRefreshToken,
setAccessTokenCookie,
setRefreshTokenCookie
)
from modules.datamodels.datamodelUam import AuthAuthority
@router.post("/custom-login")
async def custom_login(
response: Response,
username: str,
password: str
):
# Verify credentials...
user = verify_user(username, password)
# Create token data
token_data = {
"sub": user.username,
"mandateId": str(user.mandateId),
"userId": str(user.id),
"authenticationAuthority": AuthAuthority.LOCAL
}
# Create tokens
access_token, expires_at = createAccessToken(token_data)
refresh_token, _ = createRefreshToken(token_data)
# Set cookies
setAccessTokenCookie(response, access_token)
setRefreshTokenCookie(response, refresh_token)
return {"status": "success", "expires_at": expires_at.isoformat()}
Clearing Tokens (Logout)
Use the JWT service cookie clearing functions:
from modules.security.jwtService import (
clearAccessTokenCookie,
clearRefreshTokenCookie
)
@router.post("/logout")
async def logout(
response: Response,
currentUser: User = Depends(getCurrentUser)
):
# Clear cookies
clearAccessTokenCookie(response)
clearRefreshTokenCookie(response)
# Optionally revoke token in database
# interface.revokeToken(tokenId)
return {"status": "logged_out"}
API Examples
Example: Protected Endpoint
from fastapi import APIRouter, Depends, HTTPException
from modules.security.auth import getCurrentUser, limiter
from modules.datamodels.datamodelUam import User
from fastapi import Request
router = APIRouter(prefix="/api/data", tags=["Data"])
@router.get("/items")
@limiter.limit("60/minute")
async def get_items(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> list:
"""
Get items for the current user.
Requires authentication.
"""
# User is guaranteed to be authenticated and enabled
# Access user properties: currentUser.id, currentUser.mandateId, etc.
items = fetch_user_items(currentUser.id)
return items
Example: Role-Based Access
from modules.datamodels.datamodelUam import UserPrivilege
@router.delete("/admin/items/{item_id}")
async def delete_item(
item_id: str,
currentUser: User = Depends(getCurrentUser)
):
"""
Delete item (admin only).
"""
# Check user privilege
if currentUser.privilege not in [UserPrivilege.ADMIN, UserPrivilege.SYSADMIN]:
raise HTTPException(
status_code=403,
detail="Admin access required"
)
delete_item_by_id(item_id)
return {"status": "deleted"}
Example: Mandate-Scoped Access
@router.get("/mandate/data")
async def get_mandate_data(
currentUser: User = Depends(getCurrentUser)
):
"""
Get data scoped to user's mandate.
"""
# User's mandateId is automatically validated during authentication
# Token context must match user's current mandate
data = fetch_mandate_data(currentUser.mandateId)
return data
Example: CSRF Protection
For state-changing operations, ensure CSRF token is included:
@router.post("/update")
async def update_data(
request: Request,
data: dict,
currentUser: User = Depends(getCurrentUser)
):
"""
Update data (requires CSRF token).
CSRF middleware automatically validates X-CSRF-Token header.
"""
# CSRF validation happens automatically via middleware
# Just ensure client sends X-CSRF-Token header
update_user_data(currentUser.id, data)
return {"status": "updated"}
Example: Error Handling
from fastapi import HTTPException, status
@router.get("/sensitive")
async def get_sensitive_data(
currentUser: User = Depends(getCurrentUser)
):
"""
Get sensitive data with proper error handling.
"""
try:
# getCurrentUser already validates:
# - Token is valid and not expired
# - User exists and is enabled
# - Context matches (mandateId, userId)
data = fetch_sensitive_data(currentUser.id)
return data
except ValueError as e:
# Handle business logic errors
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
Related Documentation
- Security Component Documentation - Component architecture and internal workings
- Architecture Overview - Overall system architecture