minor-release
Behaviour changes (potentially breaking for operators relying on the prior
unauthenticated SSE bypass):
* SSE (`Accept: text/event-stream`) and WebSocket upgrade requests now
return 401 when no authenticated session is present. Previously the
bypass forwarded unconditionally, which let any caller reach the
backend by setting the right header. Excluded URLs are unchanged.
Operators relying on unauthenticated SSE/WS access must move the path
into ExcludedURLs.
Performance fixes (target: long-running dashboards like Grafana / ArgoCD
where many panels poll concurrently while the page stays open):
* Stop honouring isTestMode() for the singleton-token-cleanup interval
under yaegi (the Traefik plugin runtime). In production the plugin was
running a 20 Hz no-op cleanup ticker because runtime.Compiler ==
"yaegi" tripped the test-mode branch.
* processAuthorizedRequest now resolves ID-token claims at most once per
request via SessionData.GetIDTokenClaims (already cached on the
session) and reuses them for both groups/roles extraction and
header-template rendering. Previously every authenticated request
parsed the JWT twice.
* Added extractGroupsAndRolesFromClaims to drive groups/roles off
pre-parsed claims; extractGroupsAndRoles still works for tests.
* Removed the unconditional session.MarkDirty() in the header-templates
branch. Templates only mutate request headers, not session state, so
the prior MarkDirty was re-encrypting and rewriting all session
cookies on every authenticated request that used header templates.
Other:
* Added isWebSocketUpgrade (RFC 6455 handshake detection — Connection:
Upgrade + Upgrade: websocket, tolerant of multi-token Connection
headers and case).
* Renamed applySSEUserHeaders -> applyBypassUserHeaders; it now returns
bool so the dispatcher can reject unauthenticated SSE/WS with 401.
* Added tests for SSE and WS bypass covering both the auth-rejection
path and the authenticated forward path.
* Add debug logging around callback URL matching in ServeHTTP
The callback URL comparison at the core of OIDC flow had zero logging,
making it extremely difficult to diagnose redirect loop issues caused
by misconfigured callbackURL (e.g., full URL vs path-only).
Every other path comparison in ServeHTTP already logs debug info
(logout, backchannel, frontchannel, excluded URLs), but the callback
URL check was completely silent.
Added debug logs that show:
- The values being compared (request path vs configured callback)
- Whether the match succeeded or failed
- Configured redirURLPath during initialization
This would have immediately revealed the root cause of issue #1
where callbackURL was set as a full URL but compared against
req.URL.Path which only contains the path component.
Closes#3
* improve-callback-url-logging: Add init-time logging for callbackURL config
* Allow to use multiple realms
This change is a ressurection of PR #88 which can't be merged due to significant refactor of the codebase.
* Fix the autocleanup routine to handle multiple realms correctly, update tests.
* Metadata rediscovery when provider is unavailable for any reason during the start.
This one prevents the permanent 503 from the plugin when OIDC provider was for some reason unavailable during the start.
* Allow internal IPs for OIDC configuration via extra flag.
Addresses issue #97
* Allow for internal IPs in OIDC configuration.
Addresses issue #97.
* feat: Add allowPrivateIPAddresses config option for internal networks
Adds a new configuration option `allowPrivateIPAddresses` that allows
OIDC provider URLs to use private IP addresses (10.x.x.x, 172.16-31.x.x,
192.168.x.x). This is useful for internal deployments where Keycloak or
other OIDC providers run on private networks without DNS resolution.
Security considerations:
- Loopback addresses (127.0.0.1, localhost, ::1) remain blocked
- Link-local addresses (169.254.x.x) remain blocked
- Default is false (secure by default)
Fixes#97
* feat: Support non-email user identifiers for Azure AD
Add userIdentifierClaim configuration option to support Azure AD users
without email addresses. This allows using alternative JWT claims like
"sub", "oid", "upn", or "preferred_username" for user identification.
- Default behavior uses "email" claim (backward compatible)
- Falls back to "sub" claim if configured claim is missing
- allowedUsers matches against the configured claim value
- allowedUserDomains only applies when using email-based identification
Fixes#95
* Race condition on traefik pod startup
When the plugin initializes and calls GetMetadataWithRecovery():
1. Checks cache first (if metadata is cached, returns immediately)
2. Creates a retry executor with startup-optimized settings (10 attempts, 1s delays)
3. Attempts to fetch metadata from the OIDC provider
4. If the fetch fails with a retryable error (connection refused, EOF, TLS/certificate errors, Traefik default cert), it waits and retries
5. After 10 attempts or on a non-retryable error, returns the error
This allows the plugin to handle the race condition where:
- Traefik initializes the plugin before routes are established
- Traefik serves its default certificate before loading real ones
- The OIDC provider pod isn't fully ready yet
Fixes issue #90
* Race condition on traefik pod startup
When the plugin initializes and calls GetMetadataWithRecovery():
1. Checks cache first (if metadata is cached, returns immediately)
2. Creates a retry executor with startup-optimized settings (10 attempts, 1s delays)
3. Attempts to fetch metadata from the OIDC provider
4. If the fetch fails with a retryable error (connection refused, EOF, TLS/certificate errors, Traefik default cert), it waits and retries
5. After 10 attempts or on a non-retryable error, returns the error
This allows the plugin to handle the race condition where:
- Traefik initializes the plugin before routes are established
- Traefik serves its default certificate before loading real ones
- The OIDC provider pod isn't fully ready yet
Fixes issue #90
* Headers too big and 431 responses
Added new option `minimalHeaders` to reduce the size of forwarded headers from the auth middleware to backend services.
- When minimalHeaders: false (default): All headers are forwarded as before
- X-Forwarded-User (always set)
- X-Auth-Request-Redirect
- X-Auth-Request-User
- X-Auth-Request-Token (the large ID token)
- X-User-Groups, X-User-Roles (if configured)
- When minimalHeaders: true: Reduces header overhead
- X-Forwarded-User (always set)
- X-User-Groups, X-User-Roles (still forwarded if configured)
- Custom templated headers (still processed)
- Skipped: X-Auth-Request-Token, X-Auth-Request-User, X-Auth-Request-Redirect
Fixes issues #64 and #86
* Add redis support for distributed caching
* Move towards the self-provided Redis connection pool and RESP protocol implementation.
Official redis client library won't work with yaegi.
* fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.
* fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.
* fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.
* fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.
* fixup! fixup! fixup! fixup! fixup! Move towards the self-provided Redis connection pool and RESP protocol implementation. Official redis client library won't work with yaegi.
* ... and another all nighter.
* fixup! ... and another all nighter.
* fixup! fixup! ... and another all nighter.
* fixup! fixup! fixup! ... and another all nighter.
* Resolve issue #85 by adding ability to set custom claims in JWT tokens
* Remove redundant validation in auth middleware ( issue #89 )
* Add ability to set cookie prefix for session cookies ( #87 )
* fixup! Add ability to set cookie prefix for session cookies ( #87 )
* Add ability to set cookie max age - issue #91
* Potential fix for code scanning alert no. 10: Size computation for allocation may overflow
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* fixup! Merge main into 0.8.0-redis: resolve conflicts
---------
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* Add ability to disable replay protection. - This is useful for runs with multiple traefik replicas to avoid false positives and tokens re-creation.
* Enhance the CI/CD pipelines
* Increase test coverage.
* Update vendored dependencies.
* Update behaviour on forceHTTPS as per issue #82
* Automatic discovery of the scopes.
Issue #61 raised very valid concerns about users configuring scopes that are not supported by the provider.
This change introduces automatic discovery of supported scopes by fetching the provider's discovery document and filtering out unsupported scopes.
Before:
User configures: scopes: ["openid", "profile", "email", "offline_access"]
Self-hosted GitLab: "The requested scope is invalid, unknown, or malformed"
Authentication: ❌ FAILS
After:
User configures: scopes: ["openid", "profile", "email", "offline_access"]
Middleware checks discovery doc → offline_access not supported
Automatically filters to: ["openid", "profile", "email"]
Authentication: ✅ SUCCEEDS
* Resolves issue #74 by enabling user to specify expected audience in the configuration.
* Fix flaky tests.