mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-12 04:04:26 +00:00
Complete rebuild of the plugin
* Fix bug affecting Azure OIDC authentication ( and most likely others ) * Fixes issue #51 * Ensure that appended roles are unique. Update the documentation. * Improvements targetting possible memory usage spikes. * Additional fixes and cleanup * Refactoring code to fix the issues identified by the users. * Modernize run * Fieldalignment * Multiple changes to improve performance and reduce complexity. - Optimise the errors and recovery. - Deduplicate code in metadata cache. - Remove unused performance monitoring code. - Simplify session management and settings handling. * Fix claims issue. * Add ability to overwrite the default scopes in the settings file * Well.. that escalated quickly. Completely forgot that Traefik uses outdated Yaegi and requires compatibility with 1.20 ( pre-generic Go code ). * Bugfix #51: Ensures that user provided scopes overrides work. * fixup! Bugfix #51: Ensures that user provided scopes overrides work. * fixup! fixup! Bugfix #51: Ensures that user provided scopes overrides work. * Abstract the provider logic into a separate package. * Additional micro fixes and cleanups. * Simplify all the things. * fixup! Simplify all the things. * fixup! fixup! Simplify all the things. * fixup! fixup! fixup! Simplify all the things. * fixup! fixup! fixup! fixup! Simplify all the things. * ... * Cleanup tests. * fixup! Cleanup tests. * fixup! fixup! fixup! Cleanup tests. * fixup! fixup! fixup! fixup! Cleanup tests. * fixup! fixup! fixup! fixup! fixup! Cleanup tests. * Issue #53: Fix CSRF token handling in reverse proxy 1. ✅ HTTPS Detection Fixed (session.go:723) - Now uses X-Forwarded-Proto header instead of r.URL.Scheme - Properly detects HTTPS in reverse proxy environments 2. ✅ SameSite Cookie Attribute Fixed - Removed automatic SameSiteStrictMode for HTTPS (would break OAuth) - Keeps SameSiteLaxMode to allow OAuth callbacks from external domains - Only uses Strict for AJAX requests which don't involve OAuth redirects 3. ✅ Cookie Domain Handling Fixed - Now respects X-Forwarded-Host header for cookie domain - Ensures cookies are set for the public domain, not internal proxy domain 4. ✅ EnhanceSessionSecurity Properly Integrated - Function is now actually called during session save - Applies security enhancements without breaking OAuth flow Why Issue #53 Failed Before: 1. Cookies were not marked Secure in HTTPS environments (browser wouldn't send them back) 2. If they had been Secure with SameSite=Strict, Azure callbacks would still fail 3. Cookie domain might have been wrong (internal vs public domain) Why It Works Now: 1. Cookies are properly marked Secure for HTTPS 2. Uses SameSite=Lax to allow OAuth provider callbacks 3. Cookie domain uses public domain from X-Forwarded-Host 4. CSRF token persists through the entire OAuth flow * Next set of enhancements together with memory usage improvements. * Memory leak fixes and optimisations. * CSRF and Cookie Domain fixes * fixup! CSRF and Cookie Domain fixes * Metadata cache leak fix + profiling * fixup! Metadata cache leak fix + profiling * Memory leaks hunting, part 1337. * Further pursue of perfection. * fixup! Further pursue of perfection. * fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection. * Clear race conditions * fixup! Clear race conditions * Weekend fun with memory leaks * Splitting code into multiple files with reasonable testing coverage. ``` ok github.com/lukaszraczylo/traefikoidc 117.017s coverage: 72.6% of statements ok github.com/lukaszraczylo/traefikoidc/auth 0.505s coverage: 87.1% of statements ok github.com/lukaszraczylo/traefikoidc/circuit_breaker 0.283s coverage: 99.0% of statements github.com/lukaszraczylo/traefikoidc/config coverage: 0.0% of statements ok github.com/lukaszraczylo/traefikoidc/handlers 0.349s coverage: 98.2% of statements ok github.com/lukaszraczylo/traefikoidc/internal/providers (cached) coverage: 94.3% of statements ok github.com/lukaszraczylo/traefikoidc/middleware 0.808s coverage: 78.0% of statements ok github.com/lukaszraczylo/traefikoidc/recovery 0.653s coverage: 100.0% of statements ok github.com/lukaszraczylo/traefikoidc/session/chunking (cached) coverage: 87.8% of statements ok github.com/lukaszraczylo/traefikoidc/session/core (cached) coverage: 85.6% of statements ok github.com/lukaszraczylo/traefikoidc/session/crypto (cached) coverage: 81.8% of statements ok github.com/lukaszraczylo/traefikoidc/session/storage (cached) coverage: 93.5% of statements ok github.com/lukaszraczylo/traefikoidc/session/validators (cached) coverage: 98.8% of statements ```` * fixup! Splitting code into multiple files with reasonable testing coverage. * fixup! fixup! Splitting code into multiple files with reasonable testing coverage. * Weekend fun with further optimisations. * fixup! Weekend fun with further optimisations. * fixup! fixup! Weekend fun with further optimisations. * fixup! fixup! fixup! Weekend fun with further optimisations. * fixup! fixup! fixup! fixup! Weekend fun with further optimisations. * fixup! fixup! fixup! fixup! fixup! Weekend fun with further optimisations. * Pre-release cleanup. * Enhance test coverage. * fixup! Enhance test coverage. * fixup! fixup! Enhance test coverage. * fixup! fixup! fixup! Enhance test coverage.
This commit is contained in:
+181
-8
@@ -35,11 +35,8 @@ testData:
|
||||
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: # OAuth 2.0 scopes to request (default: ["openid", "email", "profile"])
|
||||
- openid
|
||||
- email
|
||||
- profile
|
||||
- roles # Include this to get role information from the provider
|
||||
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
|
||||
@@ -65,6 +62,8 @@ testData:
|
||||
- /metrics
|
||||
|
||||
headers: # Custom headers to set with templated values from claims and tokens
|
||||
# NOTE: If you encounter "can't evaluate field AccessToken in type bool" errors,
|
||||
# you may need to escape the templates. See the headers section in configuration below.
|
||||
- name: "X-User-Email"
|
||||
value: "{{.Claims.email}}"
|
||||
- name: "X-User-ID"
|
||||
@@ -78,6 +77,102 @@ testData:
|
||||
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)
|
||||
overrideScopes: false # When true, replaces default scopes instead of appending (default: false)
|
||||
refreshGracePeriodSeconds: 60 # Seconds before token expiry to attempt proactive refresh (default: 60)
|
||||
|
||||
# --- Provider Specific Configuration Examples ---
|
||||
#
|
||||
# Below are example configurations tailored for specific OIDC providers.
|
||||
# Uncomment and adapt the relevant section for your provider.
|
||||
# Remember to replace placeholder values (like client IDs, secrets, domains)
|
||||
# with your actual credentials and settings.
|
||||
#
|
||||
# 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 # This is standard for Google
|
||||
# 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: # Defaults ["openid", "profile", "email"] are handled. Plugin manages Google-specifics.
|
||||
# # Do NOT add 'offline_access' - plugin handles this.
|
||||
# allowedUserDomains: # Useful for Google Workspace users
|
||||
# - your-gsuite-domain.com
|
||||
# # Google includes 'hd' (hosted domain) claim which can be used with allowedUserDomains.
|
||||
# # Other claims like 'email', 'sub', 'name' are standard.
|
||||
# # See README.md "Provider Configuration Recommendations" for Google.
|
||||
|
||||
# --- 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"
|
||||
# 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".
|
||||
# # See README.md "Provider Configuration Recommendations" for Auth0.
|
||||
|
||||
# --- 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:
|
||||
@@ -153,11 +248,15 @@ configuration:
|
||||
scopes:
|
||||
type: array
|
||||
description: |
|
||||
The OAuth 2.0 scopes to request from the OIDC provider.
|
||||
Default: ["openid", "profile", "email"]
|
||||
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
|
||||
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
|
||||
@@ -277,6 +376,58 @@ configuration:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
headers:
|
||||
type: array
|
||||
description: |
|
||||
@@ -290,6 +441,28 @@ configuration:
|
||||
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. To fix this, you need to escape the templates
|
||||
using one of these methods:
|
||||
|
||||
1. Use YAML literal style (recommended):
|
||||
headers:
|
||||
- name: "Authorization"
|
||||
value: |
|
||||
Bearer {{.AccessToken}}
|
||||
|
||||
2. Use single quotes:
|
||||
headers:
|
||||
- name: "Authorization"
|
||||
value: 'Bearer {{.AccessToken}}'
|
||||
|
||||
3. For inline double quotes, escape the braces:
|
||||
headers:
|
||||
- name: "Authorization"
|
||||
value: "Bearer {{"{{.AccessToken}}"}}"
|
||||
|
||||
Examples:
|
||||
- name: "X-User-Email", value: "{{.Claims.email}}"
|
||||
- name: "Authorization", value: "Bearer {{.AccessToken}}"
|
||||
|
||||
Reference in New Issue
Block a user