Configuration Guide for OUA Authentication¶
This document provides comprehensive information about all configuration options available in the Organization Unified Access Authentication (OUA Auth) system.
Core Configuration¶
These are essential configuration options required for the OUA Authentication system to function properly.
Required Settings¶
| Setting | Description | Default | Example |
|---|---|---|---|
OUA_SSO_URL |
The URL of your OUA SSO server. Must use HTTPS in production. | None (Required) | 'https://sso.example.com' |
OUA_CLIENT_ID |
Your application's client ID registered with the SSO server. | None (Required) | 'your-client-id' |
Middleware Configuration¶
Add these middleware classes to your Django MIDDLEWARE setting:
MIDDLEWARE = [
# Add Django's security middleware first
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# OUA Auth middleware
'oua_auth.OUAAuthMiddleware', # Required for authentication
'oua_auth.OUAUserMiddleware', # Optional: For user data synchronization
'oua_auth.SecurityHeadersMiddleware', # Optional: For security headers
# Other middleware
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Authentication Backends¶
Add the OUA Auth backend to your AUTHENTICATION_BACKENDS setting:
AUTHENTICATION_BACKENDS = [
'oua_auth.OUAAuthBackend',
'django.contrib.auth.backends.ModelBackend', # Optional: Keep Django's default backend
]
Django Rest Framework Integration¶
If you're using Django Rest Framework, add the OUA JWT Authentication class:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'oua_auth.OUAJWTAuthentication',
'rest_framework.authentication.SessionAuthentication', # Optional
],
# ... other DRF settings
}
Database Configuration¶
Add oua_auth to your INSTALLED_APPS setting to enable models for token blacklisting and suspicious activity tracking:
INSTALLED_APPS = [
# ... other apps ...
'oua_auth',
# ... other apps ...
]
After adding the app, run migrations:
python manage.py migrate oua_auth
Access Control Configuration¶
These settings control who can access your application and with what level of privileges.
Path Exclusion¶
Specify paths that should be accessible without authentication:
# Optional: Paths to exclude from authentication
OUA_EXCLUDE_PATHS = [
'/public/',
'/api/health/',
'/static/',
'/media/',
]
Domain Restrictions¶
Control which email domains are allowed to access your application:
# Allow only these domains (if empty, all domains are allowed)
OUA_ALLOWED_DOMAINS = ['company.com', 'partner.org']
# Block these domains
OUA_RESTRICTED_DOMAINS = ['competitor.com', 'spam.org']
Admin Access Controls¶
Specify which domains and email addresses are allowed admin privileges:
# Domains allowed admin access
OUA_TRUSTED_ADMIN_DOMAINS = ['admin.company.com', 'internal.company.com']
# Specific emails allowed admin access
OUA_TRUSTED_ADMIN_EMAILS = ['admin@company.com', 'superuser@company.com']
Required Token Attributes¶
Specify which attributes must be present in JWT tokens:
# Token must include these attributes
OUA_REQUIRED_TOKEN_ATTRIBUTES = ['email', 'name', 'sub', 'groups']
Security Configuration¶
These settings control various security features of the OUA Authentication system.
Token Settings¶
Configure how tokens are generated and validated:
# For internal token generation
OUA_TOKEN_SIGNING_KEY = 'your-secure-signing-key'
# Internal token lifetime in seconds (1 hour default)
OUA_INTERNAL_TOKEN_LIFETIME = 3600
# Timeout for SSO requests in seconds
OUA_REQUEST_TIMEOUT = 5
# Static PEM public key — only used when OUA_ALLOW_STATIC_KEY_FALLBACK is True.
OUA_PUBLIC_KEY = '''
-----BEGIN PUBLIC KEY-----
...your public key here...
-----END PUBLIC KEY-----
'''
# Security gate for the static-PEM fallback (H-4).
# Default: False (fail-closed). When False, a JWKS resolution failure raises
# AuthenticationFailed immediately rather than retrying against OUA_PUBLIC_KEY.
# Set to True only if JWKS is genuinely unreachable in your deployment and you
# have set OUA_PUBLIC_KEY to a current, trusted key. A WARNING is logged every
# time the fallback path fires. Ensure JWKS reachability is the long-term goal.
OUA_ALLOW_STATIC_KEY_FALLBACK = False
OUA_REQUIRED_ACR¶
- Type:
str | list[str] - Default: unset (enforcement disabled)
- Purpose: Require a sufficient OIDC
acron access tokens. A verified token whoseacris not among the configured values is rejected with HTTP 401 and aWWW-Authenticate: Bearer error="insufficient_user_authentication", acr_values=...step-up challenge (RFC 9470). When multiple values are configured, they appear space-separated and sorted in theacr_values=header field (e.g.acr_values="mfa phrh"). Matched exactly and case-sensitively (set membership; no hierarchy). Off by default — unset changes nothing.
Rate Limiting¶
Configure rate limiting to prevent brute force attacks:
# Maximum requests per minute to the SSO server
OUA_MAX_REQUESTS_PER_MINUTE = 60
# Maximum number of authentication failures before rate limiting
OUA_MAX_AUTH_FAILURES = 5
# Time window for rate limiting in seconds
OUA_AUTH_FAILURE_WINDOW = 300
# Cache settings for rate limiting
OUA_RATELIMIT_CACHE_PREFIX = "oua_auth_failure"
OUA_RATELIMIT_CACHE_TIMEOUT = 300
Suspicious Activity Detection¶
Configure how suspicious activities are detected and handled:
# Number of suspicious activities before account lock
OUA_MAX_SUSPICIOUS_ACTIVITIES = 3
# Time window for counting suspicious activities (24 hours in seconds)
OUA_SUSPICIOUS_ACTIVITY_WINDOW = 86400
# How long accounts remain locked (24 hours in seconds)
OUA_ACCOUNT_LOCK_DURATION = 86400
# Activity types that count toward locking
OUA_SUSPICIOUS_ACTIVITY_TYPES = [
'token_reuse',
'invalid_origin',
'unusual_location',
'multiple_failed_attempts'
]
Security Headers¶
Configure security headers added by the SecurityHeadersMiddleware:
# Content Security Policy
OUA_CONTENT_SECURITY_POLICY = "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'"
# X-Frame-Options
OUA_FRAME_OPTIONS = "SAMEORIGIN"
# X-XSS-Protection
OUA_XSS_PROTECTION = "1; mode=block"
# X-Content-Type-Options
OUA_CONTENT_TYPE_OPTIONS = "nosniff"
# Referrer-Policy
OUA_REFERRER_POLICY = "strict-origin-when-cross-origin"
# Permissions-Policy
OUA_PERMISSIONS_POLICY = "geolocation=(), microphone=(), camera=(), payment=()"
# HTTP Strict Transport Security (HSTS)
OUA_ENABLE_HSTS = True # Set to True in production
OUA_HSTS_SECONDS = 31536000 # 1 year
OUA_HSTS_INCLUDE_SUBDOMAINS = True
OUA_HSTS_PRELOAD = False
# Exclude paths from security headers
OUA_SECURITY_HEADERS_EXCLUDE_PATHS = ['/api/public/', '/healthcheck/']
Logging Configuration¶
Configure logging for the OUA Authentication system:
# Django Logging Configuration
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
'json': {
'()': 'oua_auth.logging_utils.JSONFormatter',
'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
},
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'json',
},
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/path/to/your/auth.log',
'maxBytes': 10485760, # 10MB
'backupCount': 5,
'formatter': 'json',
},
},
'loggers': {
'oua_auth': {
'handlers': ['console', 'file'],
'level': 'INFO',
'propagate': False,
},
},
}
# Advanced logging configuration
OUA_REDACT_SENSITIVE_DATA = True # Redact tokens, passwords in logs
OUA_LOG_REQUEST_BODIES = False # Whether to log request bodies (potential security risk)
OUA_LOG_RESPONSE_BODIES = False # Whether to log response bodies
OUA_LOG_HEADERS = ['User-Agent', 'Referer'] # Headers to include in logs
Cache Configuration¶
The OUA Authentication system uses Django's cache framework for rate limiting and other features. Configure it according to your needs:
# Simple local memory cache (for development)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
# Redis cache (recommended for production)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://redis:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
Advanced Configuration¶
These settings are for advanced use cases and fine-tuning.
Token Validation¶
# Additional token validation options
OUA_VERIFY_TOKEN_EXPIRATION = True # Whether to check token expiration
OUA_VERIFY_TOKEN_ISSUER = True # Whether to verify token issuer
OUA_ALLOWED_TOKEN_ISSUERS = ['https://sso.company.com'] # Allowed issuers
OUA_TOKEN_LEEWAY = 10 # Seconds of leeway for token expiration checks
# Minimum seconds between forced JWKS refetches triggered by InvalidSignatureError.
# When a token fails RS256 signature verification, the SDK fetches fresh keys and
# retries once (to handle key rotation). Without a cooldown, a burst of
# bad-signature tokens (attack or stale-key client storm) would trigger an
# unbounded number of requests to the SSO authority (H-3 amplification vector).
# If a refetch was already performed within this window the error is re-raised
# immediately without contacting the SSO server again. (default: 60)
OUA_JWKS_REFETCH_COOLDOWN = 60
Remote claims (v2 slim tokens)¶
As of 0.8.0, the SDK resolves identity and authorisation claims from the SSO authority at request time rather than reading them from the access-token JWT body:
- Identity (email, preferred_username, name) — resolved via
GET /userinfo, keyed bysub. - Role + tenant (sso_role, country_id, company_id, branch_ids, role, app_code) —
resolved via
POST /introspect, keyed byjti.
Both calls are cached in the Django cache framework. On a cache hit the network round-trip
is skipped. When the SSO authority is unreachable and no cached value exists (cold
cache), authentication returns HTTP 503 + Retry-After header — never a 401 / forced
logout. Credentials and session data are preserved.
Cross-app role selection (app_roles + OUA_APPLICATION_CODE)¶
As of 1.1.0, the /introspect response may additionally carry an app_roles map — an
additive, app-keyed dictionary of every per-app assignment the user holds, where each
entry is {role, country_id, company_id, branch_ids}:
{
"active": true,
"sso_role": "USER",
"role": "<flat-role>",
"app_code": "<azp-derived-app>",
"app_roles": {
"<APP_CODE_A>": {"role": "...", "company_id": 42, "branch_ids": [7]},
"<APP_CODE_B>": {"role": "...", "company_id": 2, "branch_ids": [3]}
}
}
When app_roles is present and OUA_APPLICATION_CODE is set, the SDK resolves this
app's role and tenant scope (country_id/company_id/branch_ids) from
app_roles[OUA_APPLICATION_CODE], so a single access token can authorise the user across
multiple applications. Selection rules:
- App entry present →
role/tenant come from that entry. app_rolesabsent (older SSO that does not emit it) → the flatrole/tenant fields are used verbatim (backward compatible).OUA_APPLICATION_CODEunset → the flat fields are used (no selection performed).- This app's code is not a key in
app_roles→ the user holds no role in this app:roleisNoneand the tenant ids areNone/[]. This is the correct "no assignment here" answer, distinct from the absent-map fallback above.
sso_role (and the derived role_level) always stay global — they are the canonical
cross-cutting platform role and are never taken from a per-app entry. app_code continues
to reflect the flat, azp-derived value. The request.oua_tenant key shape is unchanged;
only the values become app-scoped when an entry is selected.
| Setting | Default | Description |
|---|---|---|
OUA_APPLICATION_CODE |
"" (empty) |
This application's code, used to select its entry from the introspect app_roles map. When empty, cross-app selection is disabled and the flat role/tenant fields are used. |
| Setting | Default | Description |
|---|---|---|
OUA_INTROSPECT_ENDPOINT |
OIDC-discovered introspection_endpoint, falling back to {OUA_SSO_URL}/api/v1/oauth/introspect |
Full URL of the OAuth introspection endpoint. Override when the OIDC discovery document does not advertise the endpoint or points to a different host. |
OUA_INTROSPECTION_CLIENT_ID |
OUA_CLIENT_ID |
Client ID presented in the Authorization: Basic header when calling /introspect. Defaults to the application's own client ID. |
OUA_INTROSPECTION_CLIENT_SECRET |
OUA_CLIENT_SECRET |
Client secret paired with OUA_INTROSPECTION_CLIENT_ID. Defaults to the application's own client secret. If neither is set, introspect calls will fail → 503 + CRITICAL log at startup. |
OUA_REMOTE_CLAIMS_CACHE_TTL |
60 |
Seconds a successfully resolved claims bundle is considered fresh. After this window a background revalidation is triggered on the next request. |
OUA_REMOTE_CLAIMS_GRACE |
300 |
Additional seconds beyond OUA_REMOTE_CLAIMS_CACHE_TTL during which a stale cached bundle is served while the SSO is unreachable (stale-grace period). Set to 0 to disable stale-grace (stricter freshness; SSO unavailability → 503 immediately after TTL). |
OUA_REMOTE_CLAIMS_TIMEOUT |
OUA_VERIFY_TIMEOUT / 5 |
Per-request HTTP timeout (seconds) for /userinfo and /introspect calls. Defaults to one-fifth of the existing JWKS verification timeout. |
OUA_FETCH_USERINFO |
True |
When True, identity claims are resolved via /userinfo. Set to False only if the SSO authority does not expose a /userinfo endpoint; in that case the SDK falls back to reading whatever identity claims remain in the JWT body. |
Consistency / revocation¶
- Local blacklist — immediate; a blacklisted
jtiis rejected before any remote call. - Role / tenant changes — propagate within ≤
OUA_REMOTE_CLAIMS_CACHE_TTLseconds (default 60 s) in steady state. During an SSO outage with stale-grace enabled, stale role/tenant data may be served for up toCACHE_TTL + GRACEseconds (default 6 min). For stricter freshness (e.g. high-privilege operations), setOUA_REMOTE_CLAIMS_CACHE_TTL = 0orOUA_REMOTE_CLAIMS_GRACE = 0.
Cache keyspace / ops note¶
Each resolved introspect bundle is stored as introspect:{jti} and each userinfo bundle
as userinfo:{sub} with a finite TTL. With many short-lived tokens the introspect:{jti}
keyspace can grow; ensure your Django cache backend is configured with an LRU eviction
policy (all entries carry a finite TTL, so a maxmemory-policy of allkeys-lru or
volatile-lru is appropriate for Redis).
OUA_REQUIRED_TOKEN_ATTRIBUTES — v2 claim names¶
When using v2 slim tokens, OUA_REQUIRED_TOKEN_ATTRIBUTES must reference only claims that
survive in the slim JWT body. The v2-surviving claims are:
sub, jti, typ, scp, azp, email_verified, exp, iat, iss, aud
Claims that moved out of the JWT (email, name, preferred_username, sso_role, country_id,
company_id, branch_ids, role, app_code) must not be listed in
OUA_REQUIRED_TOKEN_ATTRIBUTES — they are now resolved remotely and their absence from
the token is intentional.
User Creation and Mapping¶
# User field mapping from token claims to User model fields
OUA_USER_FIELD_MAPPINGS = {
'email': 'email',
'given_name': 'first_name',
'family_name': 'last_name',
'preferred_username': 'username',
}
# Whether to create users automatically if they don't exist
OUA_CREATE_USERS = True
# Whether to update existing users with information from token
OUA_UPDATE_USERS = True
# Field for storing user roles/groups from token
OUA_USER_ROLES_FIELD = 'roles'
Performance Tuning¶
# Cache lifetime for validated tokens (in seconds)
OUA_TOKEN_CACHE_LIFETIME = 60
# Maximum token size in bytes (to prevent DoS attacks)
OUA_MAX_TOKEN_SIZE = 8192
# Maximum header size in bytes
OUA_MAX_HEADER_SIZE = 16384
Environment Variables¶
For better security, many of these settings can be provided via environment variables instead of hardcoding them in your settings file:
import os
OUA_SSO_URL = os.environ.get('OUA_SSO_URL')
OUA_PUBLIC_KEY = os.environ.get('OUA_PUBLIC_KEY')
OUA_CLIENT_ID = os.environ.get('OUA_CLIENT_ID')
OUA_TOKEN_SIGNING_KEY = os.environ.get('OUA_TOKEN_SIGNING_KEY')
Configuration Templates¶
Minimal Configuration¶
# Minimal OUA Auth configuration
OUA_SSO_URL = 'https://sso.example.com'
# OUA_PUBLIC_KEY is the static-PEM fallback key. Verification uses JWKS
# (auto-discovered from OUA_SSO_URL) by default; this key is consulted ONLY
# when OUA_ALLOW_STATIC_KEY_FALLBACK = True (off by default — fail-closed).
OUA_PUBLIC_KEY = '''
-----BEGIN PUBLIC KEY-----
...your public key here...
-----END PUBLIC KEY-----
'''
OUA_CLIENT_ID = 'your-client-id'
MIDDLEWARE = [
# ... other middleware ...
'oua_auth.OUAAuthMiddleware',
]
AUTHENTICATION_BACKENDS = [
'oua_auth.OUAAuthBackend',
'django.contrib.auth.backends.ModelBackend',
]
INSTALLED_APPS = [
# ... other apps ...
'oua_auth',
]
Recommended Production Configuration¶
# Recommended production configuration
OUA_SSO_URL = 'https://sso.example.com'
OUA_PUBLIC_KEY = '''
-----BEGIN PUBLIC KEY-----
...your public key here...
-----END PUBLIC KEY-----
'''
OUA_CLIENT_ID = 'your-client-id'
OUA_TOKEN_SIGNING_KEY = 'your-secure-signing-key'
# Security settings
OUA_TRUSTED_ADMIN_DOMAINS = ['admin.company.com']
OUA_MAX_AUTH_FAILURES = 5
OUA_AUTH_FAILURE_WINDOW = 300
OUA_MAX_SUSPICIOUS_ACTIVITIES = 3
OUA_ACCOUNT_LOCK_DURATION = 86400
OUA_ENABLE_HSTS = True
# Middlewares
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# ... other middleware ...
'oua_auth.OUAAuthMiddleware',
'oua_auth.OUAUserMiddleware',
'oua_auth.SecurityHeadersMiddleware',
]
# Authentication
AUTHENTICATION_BACKENDS = [
'oua_auth.OUAAuthBackend',
'django.contrib.auth.backends.ModelBackend',
]
# Rest Framework
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'oua_auth.OUAJWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
}
# Cache (Redis recommended for production)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://redis:6379/1',
}
}
INSTALLED_APPS = [
# ... other apps ...
'oua_auth',
]