displayName: Traefik OIDC type: middleware import: github.com/lukaszraczylo/traefikoidc summary: | Universal OpenID Connect (OIDC) authentication middleware for Traefik. This middleware replaces the need for forward-auth and oauth2-proxy when using Traefik as a reverse proxy. It provides a complete OIDC authentication solution with features including domain restrictions, role-based access control, session management, comprehensive security headers, automatic token refresh, and support for all major OIDC providers with automatic configuration. 🎯 SUPPORTED PROVIDERS (Auto-Detection): ✅ Google - Full OIDC, auto-configured for Workspace ✅ Azure AD - Enterprise OIDC with tenant/group support ✅ Auth0 - Flexible OIDC with custom claims ✅ Okta - Enterprise SSO with MFA support ✅ Keycloak - Self-hosted OIDC with full customization ✅ AWS Cognito - Managed OIDC with regional endpoints ✅ GitLab - Both GitLab.com and self-hosted instances ⚠️ GitHub - OAuth 2.0 only (limited: API access, no user claims) ✅ Generic OIDC - Any RFC-compliant OIDC provider 🔧 KEY FEATURES: - Automatic provider detection and configuration - Comprehensive security headers (CSP, HSTS, CORS, custom profiles) - Domain restrictions and role-based access control - Automatic token refresh and session management - Rate limiting and brute force protection - Flexible configuration with multiple deployment scenarios - Memory-efficient operation with automatic cleanup - Extensive logging and debugging capabilities - Redis cache support for multi-replica deployments with automatic failover It supports various authentication scenarios including: - Basic authentication with customizable callback and logout URLs - Email domain restrictions to limit access to specific organizations - Role and group-based access control based on OIDC claims - Public URLs that bypass authentication (excluded paths) - Secure session management with encrypted cookies - Automatic token validation and refresh - Comprehensive security headers with multiple security profiles - Rate limiting to prevent brute force attacks - Custom headers using templated values from OIDC claims - Flexible CORS configuration for API endpoints - Configurable logging levels for debugging and monitoring testData: # Required parameters providerURL: https://accounts.google.com # Base URL of the OIDC provider clientID: 1234567890.apps.googleusercontent.com # OAuth 2.0 client identifier clientSecret: secret # OAuth 2.0 client secret callbackURL: /oauth2/callback # Path where the OIDC provider will redirect after authentication sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long # Key used to encrypt session data (must be at least 32 bytes) # Optional parameters with defaults logoutURL: /oauth2/logout # Path for handling logout requests (if not provided, it will be set to callbackURL + "/logout") postLogoutRedirectURI: /oidc/different-logout # URL to redirect to after logout (default: "/") scopes: # Additional scopes to append to defaults ["openid", "profile", "email"] - roles # Result: ["openid", "profile", "email", "roles"] allowedUserDomains: # Restricts access to specific email domains (if not provided, relies on OIDC provider) - company.com - subsidiary.com allowedUsers: # Restricts access to specific email addresses regardless of domain - specific-user@company.com - another-user@gmail.com allowedRolesAndGroups: # Restricts access to users with specific roles or groups (if not provided, no role/group restrictions) - guest-endpoints - admin - developer # Custom claim names for Auth0 and other providers with namespaced claims roleClaimName: roles # JWT claim name for extracting user roles (default: "roles") groupClaimName: groups # JWT claim name for extracting user groups (default: "groups") # ⚠️ CRITICAL for TLS termination scenarios (AWS ALB, Cloud Load Balancers, etc.) # When NOT specified in config: defaults to FALSE (Go zero value) # When running behind load balancer that terminates TLS: MUST set to TRUE # See: https://github.com/lukaszraczylo/traefikoidc/issues/82 forceHTTPS: true # Forces HTTPS scheme for redirect URIs (default when not specified: false) logLevel: debug # Sets logging verbosity: debug, info, error (default: info) rateLimit: 100 # Maximum number of requests per second (default: 100, minimum: 10) excludedURLs: # Lists paths that bypass authentication - /login # covers /login, /login/me, /login/reminder etc. - /public - /health - /metrics headers: # Custom headers to set with templated values from claims and tokens # NOTE: Use double curly braces to escape template expressions in YAML # See the headers section in configuration below for details - name: "X-User-Email" value: "{{{{.Claims.email}}}}" - name: "X-User-ID" value: "{{{{.Claims.sub}}}}" - name: "Authorization" value: "Bearer {{{{.AccessToken}}}}" - name: "X-User-Roles" value: "{{{{range $i, $e := .Claims.roles}}}}{{{{if $i}}}},{{{{end}}}}{{{{$e}}}}{{{{end}}}}" # Advanced parameters (usually discovered automatically from provider metadata) revocationURL: https://accounts.google.com/revoke # Endpoint for revoking tokens oidcEndSessionURL: https://accounts.google.com/logout # Provider's end session endpoint enablePKCE: false # Enables PKCE (Proof Key for Code Exchange) for additional security cookieDomain: "" # Explicit domain for session cookies (e.g., ".example.com" for multi-subdomain setups) cookiePrefix: "" # Custom prefix for cookie names (e.g., "_oidc_myapp_" for session isolation between middleware instances) sessionMaxAge: 86400 # Maximum session age in seconds (default: 86400 = 24 hours, 0 = use default) overrideScopes: false # When true, replaces default scopes instead of appending (default: false) refreshGracePeriodSeconds: 60 # Seconds before token expiry to attempt proactive refresh (default: 60) # Auth0 / Custom API Audience Configuration audience: "" # Custom audience for access token validation (default: clientID) strictAudienceValidation: false # Reject sessions with audience mismatch (prevents token confusion attacks) allowOpaqueTokens: false # Enable opaque (non-JWT) access token support via RFC 7662 introspection requireTokenIntrospection: false # Force introspection for opaque tokens (requires introspection endpoint) disableReplayDetection: false # Disable JTI replay detection for multi-replica deployments (default: false) # Security Headers Configuration (enabled by default with 'default' profile) securityHeaders: enabled: true profile: "default" # Options: default, strict, development, api, custom # CORS configuration for API endpoints corsEnabled: false corsAllowedOrigins: - "https://your-frontend.com" - "https://*.example.com" corsAllowCredentials: true # Cross-origin policies permissionsPolicy: "geolocation=(), camera=(), microphone=()" crossOriginEmbedderPolicy: "require-corp" crossOriginOpenerPolicy: "same-origin" crossOriginResourcePolicy: "same-origin" # Custom headers customHeaders: X-Custom-Header: "production" X-API-Version: "v1" # Example with Redis cache for multi-replica deployments testDataWithRedis: # Required OIDC parameters (same as standard configuration) providerURL: https://auth.example.com clientID: your-client-id clientSecret: your-client-secret callbackURL: /oauth2/callback sessionEncryptionKey: your-64-character-encryption-key-at-least-32-bytes # Standard optional parameters logLevel: info allowedUserDomains: - company.com # Redis cache configuration for multi-replica support redis: enabled: true # Enable Redis caching address: "redis:6379" # Redis server address password: "redis-password" # Redis authentication password db: 0 # Redis database number (0-15) keyPrefix: "traefikoidc:" # Prefix for all Redis keys cacheMode: "hybrid" # Cache mode: redis, hybrid, or memory poolSize: 20 # Maximum number of connections connectTimeout: 5 # Connection timeout in seconds readTimeout: 3 # Read operation timeout writeTimeout: 3 # Write operation timeout enableTLS: false # Use TLS for Redis connection tlsSkipVerify: false # Skip TLS certificate verification hybridL1Size: 500 # L1 cache size for hybrid mode hybridL1MemoryMB: 10 # L1 memory limit for hybrid mode enableCircuitBreaker: true # Enable circuit breaker circuitBreakerThreshold: 5 # Failures before opening circuit circuitBreakerTimeout: 60 # Timeout before retry (seconds) enableHealthCheck: true # Enable periodic health checks healthCheckInterval: 30 # Health check interval (seconds) # --- Common Configuration Examples --- # # 🔒 HIGH-SECURITY CONFIGURATION # testDataHighSecurity: # providerURL: https://login.microsoftonline.com/your-tenant-id/v2.0 # clientID: your-azure-client-id # clientSecret: your-azure-client-secret # callbackURL: /oauth2/callback # sessionEncryptionKey: "maximum-security-key-at-least-32-bytes-long" # rateLimit: 50 # Restrictive rate limiting # allowedUserDomains: ["company.com"] # Domain restriction # allowedRolesAndGroups: ["admin", "security-team"] # Role restriction # securityHeaders: # enabled: true # profile: "strict" # Maximum security headers # corsEnabled: false # No CORS in high-security mode # logLevel: info # 🧑‍💻 DEVELOPMENT CONFIGURATION # testDataDevelopment: # providerURL: https://your-dev-provider.com # clientID: dev-client-id # clientSecret: dev-client-secret # callbackURL: /oauth2/callback # sessionEncryptionKey: "development-key-at-least-32-bytes-long" # forceHTTPS: false # Allow HTTP in development # excludedURLs: ["/health", "/metrics", "/debug"] # securityHeaders: # enabled: true # profile: "development" # Relaxed security for development # corsEnabled: true # corsAllowedOrigins: ["http://localhost:*", "http://127.0.0.1:*"] # logLevel: debug # 🌐 API CONFIGURATION # testDataAPI: # providerURL: https://your-auth0-domain.auth0.com # clientID: api-client-id # clientSecret: api-client-secret # callbackURL: /oauth2/callback # sessionEncryptionKey: "api-gateway-key-at-least-32-bytes-long" # refreshGracePeriodSeconds: 120 # securityHeaders: # enabled: true # profile: "api" # corsEnabled: true # corsAllowedOrigins: ["https://app.example.com"] # corsAllowedMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] # corsAllowedHeaders: ["Authorization", "Content-Type", "X-API-Key"] # headers: # Custom headers with OIDC claims (use double curly braces) # - name: "X-User-Email" # value: "{{{{.Claims.email}}}}" # - name: "X-User-ID" # value: "{{{{.Claims.sub}}}}" # --- Provider Specific Configuration Examples --- # # This middleware supports 9+ OIDC providers with automatic detection: # ✅ Google - Full OIDC with auto-configuration # ✅ Azure AD - Enterprise OIDC with tenant support # ✅ Auth0 - Flexible OIDC with custom claims # ✅ Okta - Enterprise OIDC with MFA support # ✅ Keycloak - Self-hosted OIDC with full customization # ✅ AWS Cognito - Managed OIDC with regional endpoints # ✅ GitLab - Both GitLab.com and self-hosted # ⚠️ GitHub - OAuth 2.0 only (not OIDC, limited functionality) # ✅ Generic OIDC - Any RFC-compliant OIDC provider # # Uncomment and adapt the relevant section for your provider. # Remember to replace placeholder values with your actual credentials. # For all providers, ensure claims like email, roles, and groups are # configured to be included in the ID TOKEN (this plugin validates ID tokens). # --- Keycloak Example --- # testDataKeycloak: # providerURL: https://your-keycloak-domain/realms/your-realm # e.g., http://localhost:8080/realms/master # clientID: your-keycloak-client-id # clientSecret: your-keycloak-client-secret # Store securely, e.g., urn:k8s:secret:namespace:secret-name:key # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-keycloak" # scopes: # Default ["openid", "profile", "email"] are usually sufficient. Add others if mappers depend on them. # - roles # Example: if you mapped Keycloak roles to a 'roles' claim in the ID token # - groups # Example: if you mapped Keycloak groups to a 'groups' claim in the ID token # allowedRolesAndGroups: # Corresponds to 'Token Claim Name' in Keycloak mappers # - admin # - editor # # Ensure Keycloak client mappers add 'email', 'roles', 'groups' etc. to the ID Token. # # See README.md "Provider Configuration Recommendations" for Keycloak. # --- Azure AD (Microsoft Entra ID) Example --- # testDataAzureAD: # providerURL: https://login.microsoftonline.com/your-tenant-id/v2.0 # Replace your-tenant-id # clientID: your-azure-ad-client-id # clientSecret: your-azure-ad-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-azure" # scopes: # Defaults ["openid", "profile", "email"] are good. # # Azure AD may require specific scopes for certain graph API permissions if you were to use the access token, # # but for ID token claims, defaults are often enough. # # Group claims need to be configured in Azure AD App Registration -> Token Configuration -> Add groups claim. # allowedUserDomains: # - yourcompany.com # allowedRolesAndGroups: # If you configured group claims (typically 'groups') or app roles in Azure AD # - "group-object-id-1" # Azure AD group claims can be Object IDs by default # - "AppRoleName" # # See README.md "Provider Configuration Recommendations" for Azure AD. # --- Google Workspace / Google Cloud Identity Example --- # testDataGoogle: # providerURL: https://accounts.google.com # Standard Google OIDC endpoint # clientID: your-google-client-id.apps.googleusercontent.com # clientSecret: your-google-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-google" # scopes: # Auto-detects Google and applies proper configuration # # Do NOT add 'offline_access' - plugin automatically handles Google-specific parameters # allowedUserDomains: # Useful for Google Workspace domain restriction # - your-gsuite-domain.com # refreshGracePeriodSeconds: 300 # Optional: Refresh 5 min before expiry # # Google auto-config: Uses access_type=offline, prompt=consent, filters unsupported scopes # # Available claims: email, sub, name, given_name, family_name, picture, hd (hosted domain) # --- Okta Example --- # testDataOkta: # providerURL: https://your-tenant.okta.com/oauth2/default # Use your Okta domain and auth server # clientID: your-okta-client-id # clientSecret: your-okta-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-okta" # scopes: # - groups # Include for group-based access control # allowedRolesAndGroups: # - admin # - developer # - "Everyone" # Default Okta group # # Okta config: Create OIDC Web App in admin console, configure Groups claim # # Available claims: email, sub, name, groups, custom attributes # --- AWS Cognito Example --- # testDataCognito: # providerURL: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_YourUserPool # Regional endpoint # clientID: your-cognito-client-id # clientSecret: your-cognito-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-cognito" # scopes: # - aws.cognito.signin.user.admin # Cognito-specific scope # allowedRolesAndGroups: # - admin # - user # # Cognito config: Create User Pool, App Client with authorization code grant # # Available claims: email, sub, cognito:username, cognito:groups, custom attributes # --- GitLab Example --- # testDataGitLab: # providerURL: https://gitlab.com # For GitLab.com, or use your self-hosted URL # clientID: your-gitlab-client-id # clientSecret: your-gitlab-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-gitlab" # scopes: # - read_user # - read_api # For GitLab API access # allowedUserDomains: # - yourcompany.com # Optional domain restriction # # GitLab config: Create application in GitLab Admin Area > Applications # # Available claims: email, sub, name, nickname, preferred_username # --- GitHub OAuth 2.0 Example (⚠️ Limited Functionality) --- # testDataGitHub: # providerURL: https://github.com/login/oauth # GitHub OAuth endpoint (NOT OIDC) # clientID: your-github-client-id # clientSecret: your-github-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-github" # scopes: # - user:email # - read:user # # ⚠️ IMPORTANT: GitHub uses OAuth 2.0, NOT OpenID Connect # # - No ID tokens available (access tokens only) # # - No refresh tokens (users must re-authenticate when tokens expire) # # - No standard OIDC claims # # - Use only for GitHub API access, not for user authentication with claims # # GitHub config: Create OAuth App in GitHub Settings > Developer settings # --- Auth0 Example --- # testDataAuth0: # providerURL: https://your-auth0-domain.auth0.com # Replace with your Auth0 domain # clientID: your-auth0-client-id # clientSecret: your-auth0-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-auth0" # # # Auth0 Audience Configuration (for custom APIs) # # Scenario 1 (Recommended): Custom API with JWT access tokens # audience: "https://my-api.example.com" # Your API identifier from Auth0 dashboard # strictAudienceValidation: true # Enforce proper audience validation for security # # # Scenario 2 (Backward Compatible): Default audience (uses client_id) # # audience: "" # Leave empty or omit to use client_id as audience # # strictAudienceValidation: false # Allows fallback to ID token validation (logs warnings) # # # Scenario 3: Opaque (non-JWT) access tokens # # allowOpaqueTokens: true # Enable opaque token support # # requireTokenIntrospection: true # Require RFC 7662 token introspection # # scopes: # Defaults ["openid", "profile", "email"]. Add custom scopes if your Auth0 Rules/Actions require them. # - read:custom_data # Example custom scope # allowedRolesAndGroups: # Based on claims added via Auth0 Rules or Actions (e.g. namespaced claims) # - "https://your-app.com/roles:admin" # - editor # # Use Auth0 Rules or Actions to add custom claims (roles, permissions) to the ID Token. # # Ensure postLogoutRedirectURI is in Auth0 app's "Allowed Logout URLs". # # For detailed Auth0 audience configuration, see AUTH0_AUDIENCE_GUIDE.md # --- Generic OIDC Provider Example --- # testDataGenericOIDC: # providerURL: https://your-generic-oidc-provider.com/oidc # Issuer URL for your provider # clientID: your-generic-client-id # clientSecret: your-generic-client-secret # Store securely # callbackURL: /oauth2/callback # sessionEncryptionKey: "a-very-secure-key-at-least-32-bytes-long-for-generic" # scopes: # Must include "openid". "profile" and "email" are common. # - openid # - profile # - email # - custom_scope_for_claims # If your provider needs specific scopes for ID token claims # allowedRolesAndGroups: # - user_role_from_id_token # # Consult your provider's documentation on how to map attributes/roles/groups to ID Token claims. # # Verify ID Token contents (e.g. jwt.io) to see available claims. # # See README.md "Provider Configuration Recommendations" for Generic OIDC. # Configuration documentation configuration: providerURL: type: string description: | The base URL of the OIDC provider. This is the issuer URL that will be used to discover OIDC endpoints like authorization, token, and JWKS URIs. Supported providers (auto-detected from URL): - https://accounts.google.com (Google) - https://login.microsoftonline.com/tenant-id/v2.0 (Azure AD) - https://your-auth0-domain.auth0.com (Auth0) - https://your-tenant.okta.com/oauth2/default (Okta) - https://your-keycloak/auth/realms/your-realm (Keycloak) - https://cognito-idp.region.amazonaws.com/pool-id (AWS Cognito) - https://gitlab.com (GitLab) - https://github.com/login/oauth (GitHub - OAuth 2.0 only) - Any RFC-compliant OIDC provider (Generic) required: true clientID: type: string description: | The OAuth 2.0 client identifier obtained from your OIDC provider. This is the public identifier for your application. required: true clientSecret: type: string description: | The OAuth 2.0 client secret obtained from your OIDC provider. This should be kept confidential and not exposed in client-side code. For Kubernetes deployments, you can use the secret reference format: urn:k8s:secret:namespace:secret-name:key required: true callbackURL: type: string description: | The path where the OIDC provider will redirect after authentication. This must match one of the redirect URIs configured in your OIDC provider. The full redirect URI will be constructed as: [scheme]://[host][callbackURL] Example: /oauth2/callback required: true sessionEncryptionKey: type: string description: | Key used to encrypt session data stored in cookies. Must be at least 32 bytes long for security. Example: potato-secret-is-at-least-32-bytes-long required: true logoutURL: type: string description: | The path for handling logout requests. If not provided, it will be set to callbackURL + "/logout". Example: /oauth2/logout required: false postLogoutRedirectURI: type: string description: | The URL to redirect to after logout. Default: "/" Example: /logged-out-page required: false scopes: type: array description: | Additional OAuth 2.0 scopes to append to the default scopes. Default scopes are always included: ["openid", "profile", "email"] User-provided scopes are appended to defaults with automatic deduplication. For example, specifying ["roles", "custom_scope"] results in: ["openid", "profile", "email", "roles", "custom_scope"] Include "roles" or similar scope if you need role/group information. Note: For Google OAuth, the middleware automatically handles the proper authentication parameters and does NOT require the "offline_access" scope (which Google rejects as invalid). See documentation for details. required: false items: type: string logLevel: type: string description: | Sets the logging verbosity. Valid values: "debug", "info", "error" Default: "info" required: false enum: - debug - info - error forceHTTPS: type: boolean description: | Forces HTTPS scheme for redirect URIs regardless of request headers or TLS state. ⚠️ CRITICAL CONFIGURATION for TLS Termination Scenarios: When running Traefik behind a load balancer that terminates TLS (AWS ALB, Google Cloud Load Balancer, Azure Application Gateway, etc.), you MUST set this to true. Without it, redirect URIs will use http:// instead of https://, causing OAuth callback failures. How it works: - When true: Always uses https:// for redirect URIs (highest priority) - When false: Detects scheme from X-Forwarded-Proto header or TLS state - When NOT specified: Defaults to false (Go zero value for bool) Default: false (when not specified in configuration) Recommended: true (for production environments and TLS termination scenarios) See: https://github.com/lukaszraczylo/traefikoidc/issues/82 required: false rateLimit: type: integer description: | Sets the maximum number of requests per second. This helps prevent brute force attacks. Default: 100 Minimum: 10 required: false excludedURLs: type: array description: | Lists paths that bypass authentication. These paths will be accessible without OIDC authentication. The middleware uses prefix matching, so "/public" will match "/public", "/public/page", "/public-data", etc. Examples: ["/health", "/metrics", "/public"] required: false items: type: string allowedUserDomains: type: array description: | Restricts access to users with email addresses from specific domains. If not provided, the middleware relies entirely on the OIDC provider for authentication decisions. Examples: ["company.com", "subsidiary.com"] required: false items: type: string allowedUsers: type: array description: | Restricts access to specific email addresses. If provided, only users with these exact email addresses will be allowed access, in addition to any domain-level restrictions set by allowedUserDomains. This provides fine-grained control over individual access and can be used together with allowedUserDomains for flexible access control strategies. Examples: ["user1@example.com", "admin@company.com"] required: false items: type: string allowedRolesAndGroups: type: array description: | Restricts access to users with specific roles or groups. If not provided, no role/group restrictions are applied. The middleware checks both the "roles" and "groups" claims in the ID token. Examples: ["admin", "developer"] required: false items: type: string revocationURL: type: string description: | The endpoint for revoking tokens. If not provided, it will be discovered from provider metadata. Example: https://accounts.google.com/revoke required: false oidcEndSessionURL: type: string description: | The provider's end session endpoint. If not provided, it will be discovered from provider metadata. Example: https://accounts.google.com/logout required: false enablePKCE: type: boolean description: | Enables PKCE (Proof Key for Code Exchange) for the OAuth 2.0 authorization code flow. PKCE adds an extra layer of security to protect against authorization code interception attacks. Not all OIDC providers support PKCE, so this should only be enabled if your provider supports it. If enabled, the middleware will generate and use a code verifier/challenge pair during authentication. Default: false required: false cookieDomain: type: string description: | Explicit domain for session cookies. This is important for multi-subdomain setups and reverse proxy deployments to ensure consistent cookie handling. When set, all session cookies will use this domain. When not set, the domain is auto-detected from the request headers (X-Forwarded-Host or Host). Use a leading dot for subdomain-wide cookies (e.g., ".example.com" allows cookies to be shared between app.example.com, api.example.com, etc.). Use a specific domain for host-only cookies (e.g., "app.example.com" restricts cookies to that exact domain). This setting is crucial to prevent authentication issues like "CSRF token missing in session" errors that can occur when cookies are created with inconsistent domains. Examples: - ".example.com" - Allows all subdomains to share cookies - "app.example.com" - Restricts cookies to this specific host Default: "" (auto-detected from request headers) required: false cookiePrefix: type: string description: | Custom prefix for session cookie names. This is essential for running multiple middleware instances with different authorization requirements on the same domain. By default, all middleware instances use the same cookie names (_oidc_raczylo_m, _oidc_raczylo_a, etc.), which means they share session state. When you have multiple instances with different access restrictions (e.g., one for general users and one for admins), this session sharing can lead to authorization bypass issues. Setting a unique cookiePrefix for each middleware instance ensures complete session isolation, preventing users authenticated via one middleware from automatically gaining access to routes protected by a different middleware. The prefix is prepended to all session cookie names: - Main session cookie: {prefix}m - Access token cookie: {prefix}a - Refresh token cookie: {prefix}r - ID token cookie: {prefix}id Examples: - "_oidc_userauth_" - For general user authentication middleware - "_oidc_adminauth_" - For admin-only authentication middleware - "_oidc_api_" - For API-specific authentication middleware Security Note: Use different cookie prefixes AND different sessionEncryptionKey values for each middleware instance to ensure complete isolation. Default: "_oidc_raczylo_" (standard prefix for backward compatibility) See: https://github.com/lukaszraczylo/traefikoidc/issues/87 required: false sessionMaxAge: type: integer description: | Maximum session age in seconds before requiring re-authentication. This setting controls how long a user's authentication session remains valid before they must authenticate again through the OIDC provider. The session age is tracked from the initial authentication time (created_at). When a session exceeds this age: - The session is cleared and invalidated - The user is redirected to re-authenticate - All session cookies are removed Use Cases: - High-security applications: Use shorter durations (e.g., 3600 = 1 hour) - Standard applications: Default 24 hours balances security and UX - Long-lived sessions: Extend for applications accessed infrequently (e.g., 604800 = 7 days, 2592000 = 30 days) Security Considerations: - Shorter sessions provide better security but require more frequent logins - Longer sessions improve user experience but increase security risk - Consider your application's security requirements and user access patterns - This is independent of token refresh - tokens can be refreshed during the session Common Values: - 3600 (1 hour) - High security applications - 28800 (8 hours) - Working day session - 86400 (24 hours) - Default, balances security and convenience - 604800 (7 days) - Weekly session for less frequently accessed apps - 2592000 (30 days) - Monthly session for infrequently used applications Default: 86400 (24 hours) Minimum: 0 (uses default of 24 hours) See: https://github.com/lukaszraczylo/traefikoidc/issues/91 required: false overrideScopes: type: boolean description: | When set to true, the scopes you provide will completely replace the default scopes (openid, profile, email) instead of being appended to them. This is useful when you need precise control over the scopes sent to the OIDC provider, such as when a provider requires specific scopes or when you want to minimize the requested permissions. Default: false (appends user scopes to defaults) required: false refreshGracePeriodSeconds: type: integer description: | The number of seconds before a token expires to attempt proactive refresh. When a request is made and the access token will expire within this grace period, the middleware will attempt to refresh the token proactively. This helps prevent authentication interruptions for active users. Setting this to 0 disables proactive refresh (tokens are only refreshed after expiry). Default: 60 (1 minute before expiry) required: false audience: type: string description: | Custom audience value for access token validation. This configures the expected audience claim in access tokens. Per OAuth 2.0 and OIDC specifications: - ID tokens always have aud=client_id (per OIDC Core 1.0) - Access tokens can have custom audiences (e.g., API identifiers) Auth0 Scenarios: 1. Custom API audience (recommended): Set to your API identifier from Auth0 Example: "https://my-api.example.com" Result: Access tokens will contain this audience 2. Default audience: Leave empty or omit (uses client_id) Result: Access tokens may not contain client_id, triggering warnings 3. Opaque tokens: Use with allowOpaqueTokens=true for non-JWT tokens When configured and different from client_id, the middleware automatically adds the audience parameter to the authorize endpoint request. Default: "" (uses client_id as audience) See: AUTH0_AUDIENCE_GUIDE.md for detailed configuration required: false strictAudienceValidation: type: boolean description: | Enforce strict audience validation for access tokens. When enabled, sessions are rejected if access token validation fails due to audience mismatch. This prevents falling back to ID token validation, addressing potential token confusion attacks where tokens intended for different APIs could be used to grant access. Auth0 Scenario 2 Protection: - When true: Rejects sessions with mismatched access token audience - When false: Logs security warnings but allows fallback to ID token (backward compatible) Security Recommendation: - Production environments: Set to true for maximum security - Development/testing: Can use false with monitoring of security warnings This setting addresses security concerns where access tokens without proper audience claims could bypass API-specific authorization checks. Default: false (backward compatible) See: https://github.com/lukaszraczylo/traefikoidc/issues/74 required: false allowOpaqueTokens: type: boolean description: | Enable acceptance of opaque (non-JWT) access tokens. When enabled, the middleware accepts access tokens that are not in JWT format (3-part base64 structure). Opaque tokens are validated using OAuth 2.0 Token Introspection (RFC 7662) if the provider exposes an introspection endpoint. Auth0 Scenario 3: Some Auth0 configurations issue opaque access tokens when no default API is configured. This setting allows those tokens to be validated. Requirements: - Provider must support introspection_endpoint in OIDC discovery - Client must have appropriate introspection permissions Validation Process: 1. Detects opaque token (not 3-part JWT structure) 2. Calls provider's introspection endpoint with client credentials 3. Validates response (active status, expiration, audience if present) 4. Caches result for 5 minutes or token expiry (whichever is shorter) 5. Falls back to ID token validation if introspection unavailable (unless requireTokenIntrospection=true) Default: false (only JWT access tokens accepted) See: AUTH0_AUDIENCE_GUIDE.md for configuration examples required: false requireTokenIntrospection: type: boolean description: | Require token introspection for all opaque access tokens. When enabled with allowOpaqueTokens=true, opaque tokens are rejected if: - Introspection endpoint is not available from provider metadata - Introspection request fails - Introspection response indicates token is not active Security Levels: - requireTokenIntrospection=true + allowOpaqueTokens=true: Maximum security - rejects opaque tokens without successful introspection - requireTokenIntrospection=false + allowOpaqueTokens=true: Backward compatible - falls back to ID token validation if introspection fails - requireTokenIntrospection=true + allowOpaqueTokens=false: No effect - opaque tokens are already rejected Recommended Configuration: When accepting opaque tokens, always set this to true for maximum security: ```yaml allowOpaqueTokens: true requireTokenIntrospection: true ``` Default: false (allows fallback to ID token) See: RFC 7662 OAuth 2.0 Token Introspection specification required: false disableReplayDetection: type: boolean description: | Disable JTI-based replay attack detection for multi-replica deployments. When running multiple Traefik replicas, each instance maintains its own in-memory JTI (JWT Token ID) cache. This causes false positives when the same valid token hits different replicas: - Request → Replica A → JTI added to cache → OK - Request → Replica B → JTI not in Replica B's cache → OK - Request → Replica A again → JTI found → FALSE POSITIVE "replay detected" Security Impact: When disabled, the following validations remain active: - RSA/ECDSA signature verification - Token expiration (exp claim) - Issuer validation (iss claim) - Audience validation (aud claim) - Not-before validation (nbf claim) - Issued-at validation (iat claim) Only the JTI replay check is skipped. Recommendations: - Single-instance deployment: false (default, enables replay protection) - Multi-replica deployment: true (prevents false positives) - Production with shared cache: false (use Redis/Memcached for shared JTI cache) Default: false (replay detection enabled) required: false headers: type: array description: | Custom HTTP headers to set with templated values derived from OIDC claims and tokens. Each header has a name and a value template that can access: - {{.Claims.field}} - Access ID token claims (e.g., email, sub, name) - {{.AccessToken}} - The raw access token string - {{.IdToken}} - The raw ID token string - {{.RefreshToken}} - The raw refresh token string Templates support Go template syntax including conditionals and iteration. Variable names are case-sensitive - use .Claims not .claims. IMPORTANT: Template Escaping If you encounter the error "can't evaluate field AccessToken in type bool" when starting Traefik, this means Traefik is trying to evaluate the template expressions before passing them to the plugin. SOLUTION: You must escape the template expressions using double curly braces: headers: - name: "Authorization" value: "Bearer {{{{.AccessToken}}}}" This is the only reliable method that works consistently. Here's why: - The YAML parser converts {{{{ → {{ and }}}} → }} - Result: Bearer {{.AccessToken}} reaches the Go template engine correctly - Other methods (YAML literal style, single quotes) do NOT work reliably Examples: - name: "X-User-Email", value: "{{{{.Claims.email}}}}" - name: "Authorization", value: "Bearer {{{{.AccessToken}}}}" - name: "X-User-Roles", value: "{{{{range $i, $e := .Claims.roles}}}}{{{{if $i}}}},{{{{end}}}}{{{{$e}}}}{{{{end}}}}" required: false items: type: object properties: name: type: string description: The HTTP header name to set value: type: string description: Template string for the header value securityHeaders: type: object description: | Configuration for security headers to protect against common web vulnerabilities. Security headers are applied to all authenticated responses. The middleware includes comprehensive security headers support with multiple profiles: - default: Balanced security for standard web applications - strict: Maximum security for high-security applications - development: Relaxed policies for local development - api: API-friendly configuration with CORS support - custom: Full control over all security header settings Security features include: - Content Security Policy (CSP) to prevent XSS attacks - HTTP Strict Transport Security (HSTS) to enforce HTTPS - Frame Options to prevent clickjacking - XSS Protection for browser-level filtering - Content Type Options to prevent MIME sniffing - CORS headers for cross-origin resource sharing - Custom headers for additional security requirements Example configurations: Basic security (recommended): securityHeaders: enabled: true profile: "default" API with CORS: securityHeaders: enabled: true profile: "api" corsEnabled: true corsAllowedOrigins: ["https://app.example.com"] Custom configuration: securityHeaders: enabled: true profile: "custom" contentSecurityPolicy: "default-src 'self'" corsEnabled: true corsAllowedOrigins: ["https://*.example.com"] customHeaders: X-Security-Level: "high" required: false properties: enabled: type: boolean description: | Enable or disable security headers. When disabled, only basic fallback headers are applied. Default: true required: false profile: type: string description: | Security profile to use. Each profile provides a different balance of security and functionality: - default: Balanced security suitable for most web applications - strict: Maximum security with very restrictive policies - development: Relaxed policies for local development (enables localhost CORS) - api: API-friendly configuration with configurable CORS - custom: No defaults, use only explicitly configured settings Default: "default" required: false enum: - default - strict - development - api - custom contentSecurityPolicy: type: string description: | Content Security Policy header value to prevent XSS and code injection attacks. Only applied when using "custom" profile or to override profile defaults. Examples: - "default-src 'self'" (strict) - "default-src 'self'; script-src 'self' 'unsafe-inline'" (moderate) - "default-src 'self' 'unsafe-inline' 'unsafe-eval'" (permissive) required: false strictTransportSecurity: type: boolean description: | Enable HTTP Strict Transport Security (HSTS) to force HTTPS connections. Only applied when HTTPS is detected (via TLS or X-Forwarded-Proto header). Default: true required: false strictTransportSecurityMaxAge: type: integer description: | HSTS max-age value in seconds. Determines how long browsers should enforce HTTPS. Common values: - 31536000 (1 year) - recommended for production - 86400 (1 day) - for testing Default: 31536000 required: false strictTransportSecuritySubdomains: type: boolean description: | Include subdomains in HSTS policy. When true, HSTS applies to all subdomains of the current domain. Default: true required: false strictTransportSecurityPreload: type: boolean description: | Enable HSTS preload list eligibility. Allows the domain to be included in browser HSTS preload lists. Default: true required: false frameOptions: type: string description: | X-Frame-Options header value to prevent clickjacking attacks. Options: - DENY: Prevents framing completely - SAMEORIGIN: Allows framing only from the same origin - ALLOW-FROM uri: Allows framing from specific URI Default: "DENY" required: false contentTypeOptions: type: string description: | X-Content-Type-Options header value to prevent MIME type sniffing. Should typically be set to "nosniff". Default: "nosniff" required: false xssProtection: type: string description: | X-XSS-Protection header value for browser XSS filtering. Recommended value: "1; mode=block" Default: "1; mode=block" required: false referrerPolicy: type: string description: | Referrer-Policy header value to control referrer information sharing. Common values: - strict-origin-when-cross-origin (recommended) - no-referrer (most restrictive) - same-origin (moderate) Default: "strict-origin-when-cross-origin" required: false corsEnabled: type: boolean description: | Enable Cross-Origin Resource Sharing (CORS) headers. Essential for API endpoints that need to be accessed from web browsers. Default: false required: false corsAllowedOrigins: type: array description: | List of allowed origins for CORS requests. Supports wildcards for flexible origin matching: - "https://example.com" (exact match) - "https://*.example.com" (subdomain wildcard) - "http://localhost:*" (port wildcard, useful for development) - "*" (allow all origins - not recommended for production) Examples: ["https://app.example.com", "https://*.api.example.com"] required: false items: type: string corsAllowedMethods: type: array description: | HTTP methods allowed for CORS requests. Default: ["GET", "POST", "OPTIONS"] Common additions: ["PUT", "DELETE", "PATCH"] required: false items: type: string corsAllowedHeaders: type: array description: | HTTP headers allowed for CORS requests. Default: ["Authorization", "Content-Type"] Common additions: ["X-Requested-With", "X-API-Key"] required: false items: type: string corsAllowCredentials: type: boolean description: | Allow credentials (cookies, authorization headers) in CORS requests. Required for authenticated API requests from browsers. Default: false required: false corsMaxAge: type: integer description: | Maximum age in seconds for CORS preflight cache. Reduces preflight request frequency for better performance. Default: 86400 (24 hours) required: false customHeaders: type: object description: | Additional custom headers to include in responses. Useful for application-specific security requirements. Examples: X-Security-Level: "high" X-API-Version: "v1" X-Environment: "production" required: false disableServerHeader: type: boolean description: | Remove the Server header to hide server information. Recommended for security through obscurity. Default: true required: false disablePoweredByHeader: type: boolean description: | Remove the X-Powered-By header to hide technology stack information. Default: true required: false permissionsPolicy: type: string description: | Permissions-Policy header to control browser feature permissions. This header allows you to control which features and APIs can be used. Examples: - "geolocation=(), camera=(), microphone=()" (deny all) - "geolocation=(self), camera=()" (allow geolocation for same origin only) Common directives: accelerometer, camera, geolocation, gyroscope, magnetometer, microphone, payment, usb required: false crossOriginEmbedderPolicy: type: string description: | Cross-Origin-Embedder-Policy (COEP) header to prevent untrusted resources from being loaded. Options: - "require-corp": Resources must explicitly grant permission - "credentialless": Load without credentials for cross-origin resources - "unsafe-none": No restrictions (default) Required for certain browser features like SharedArrayBuffer. required: false crossOriginOpenerPolicy: type: string description: | Cross-Origin-Opener-Policy (COOP) header to isolate browsing context from cross-origin windows. Options: - "same-origin": Isolate from cross-origin documents - "same-origin-allow-popups": Allow popups that don't set COOP - "unsafe-none": No isolation (default) Helps prevent cross-origin attacks and Spectre-like vulnerabilities. required: false crossOriginResourcePolicy: type: string description: | Cross-Origin-Resource-Policy (CORP) header to control which origins can load this resource. Options: - "same-origin": Only same-origin requests can load the resource - "same-site": Only same-site requests can load the resource - "cross-origin": Any origin can load the resource (default) Prevents your resources from being embedded on other sites. required: false redis: type: object description: | Optional Redis cache configuration for multi-replica deployments. When running multiple Traefik instances, Redis provides shared caching to: - Prevent JTI replay detection false positives across replicas - Share token verification results between instances - Maintain consistent session state across the cluster - Improve performance by reducing redundant OIDC provider calls Features: - Automatic failover to memory-only mode when Redis is unavailable - Circuit breaker pattern for resilience against Redis failures - Health checking with automatic recovery - Multiple cache modes: redis-only, hybrid (L1 memory + L2 Redis), memory-only - Configurable timeouts and connection pooling - TLS support for secure Redis connections The middleware gracefully handles Redis failures by falling back to in-memory caching, ensuring your authentication flow continues even during Redis outages. Example configuration: ```yaml redis: enabled: true address: "redis:6379" cacheMode: "hybrid" enableCircuitBreaker: true ``` required: false properties: enabled: type: boolean description: | Enable Redis caching for distributed session and token management. When enabled, the middleware will attempt to connect to Redis and use it for shared state across multiple Traefik instances. Default: false required: false address: type: string description: | Redis server address in host:port format. Examples: - "redis:6379" (Docker/Kubernetes service) - "localhost:6379" (local Redis) - "redis.example.com:6380" (custom host/port) - "redis-cluster.default.svc.cluster.local:6379" (Kubernetes) Required when Redis is enabled. required: false password: type: string description: | Password for Redis authentication. Leave empty if Redis doesn't require authentication. For Kubernetes deployments, you can use secret references: urn:k8s:secret:namespace:secret-name:key Default: "" (no authentication) required: false db: type: integer description: | Redis database number to use (0-15). Different databases can be used to isolate data between environments. Default: 0 required: false keyPrefix: type: string description: | Prefix for all Redis keys created by this middleware. Useful for: - Avoiding key collisions with other applications - Identifying keys for monitoring/debugging - Supporting multiple environments in the same Redis instance Default: "traefikoidc:" required: false cacheMode: type: string description: | Determines the caching strategy: - "redis": Redis-only caching. All cache operations go directly to Redis. Best for: Consistent state across all replicas, minimal memory usage. - "hybrid": Two-tier caching with in-memory L1 and Redis L2. Best for: High performance with shared state, reduced Redis load. L1 provides fast local cache, L2 provides shared state. - "memory": Memory-only caching (Redis disabled even if configured). Best for: Single instance deployments, development/testing. Default: "redis" (when Redis is enabled) required: false enum: - redis - hybrid - memory poolSize: type: integer description: | Maximum number of socket connections to Redis. Higher values allow more concurrent operations but consume more resources. Recommendations: - Small deployments: 10-20 - Medium deployments: 20-50 - Large deployments: 50-100 Default: 10 required: false connectTimeout: type: integer description: | Timeout in seconds for establishing new connections to Redis. Should be higher than network latency but low enough to fail fast. Default: 5 seconds required: false readTimeout: type: integer description: | Timeout in seconds for Redis read operations. Includes the time to send the command, wait for Redis to process it, and receive the response. Default: 3 seconds required: false writeTimeout: type: integer description: | Timeout in seconds for Redis write operations. Should account for network latency and Redis persistence settings. Default: 3 seconds required: false enableTLS: type: boolean description: | Enable TLS encryption for Redis connections. Required when connecting to Redis instances that enforce TLS, such as AWS ElastiCache with encryption in transit. Default: false required: false tlsSkipVerify: type: boolean description: | Skip TLS certificate verification for Redis connections. ⚠️ WARNING: Only use in development environments. This option bypasses certificate validation and should never be used in production as it's vulnerable to man-in-the-middle attacks. Default: false required: false hybridL1Size: type: integer description: | Maximum number of items in the L1 (in-memory) cache for hybrid mode. Controls how many cache entries are kept in local memory before eviction. Only applies when cacheMode is "hybrid". Default: 500 required: false hybridL1MemoryMB: type: integer description: | Maximum memory in megabytes for L1 cache in hybrid mode. The cache will start evicting items when this limit is approached. Only applies when cacheMode is "hybrid". Default: 10 MB required: false enableCircuitBreaker: type: boolean description: | Enable circuit breaker pattern for Redis connection failures. When enabled, the middleware will: 1. Track Redis operation failures 2. Open the circuit after threshold failures (stop trying Redis) 3. Fall back to in-memory caching 4. Periodically attempt to reconnect (half-open state) 5. Resume Redis operations when connection recovers This prevents cascading failures and improves resilience. Default: true required: false circuitBreakerThreshold: type: integer description: | Number of consecutive Redis failures before opening the circuit. Lower values make the system more sensitive to Redis issues, higher values tolerate more failures before switching to fallback. Default: 5 required: false circuitBreakerTimeout: type: integer description: | Time in seconds to wait before attempting to close the circuit. After this timeout, the circuit breaker will allow one test request to Redis. If successful, normal operations resume. Default: 60 seconds required: false enableHealthCheck: type: boolean description: | Enable periodic health checks for Redis connection. Health checks: - Run in the background at regular intervals - Detect Redis availability without affecting request processing - Automatically reconnect when Redis becomes available - Update circuit breaker state based on health status Default: true required: false healthCheckInterval: type: integer description: | Interval in seconds between Redis health checks. Lower values detect issues faster but increase Redis load. Higher values reduce overhead but delay failure detection. Default: 30 seconds required: false