This middleware replaces the need for forward-auth and oauth2-proxy when using Traefik as a reverse proxy to support OpenID Connect (OIDC) authentication.
The Traefik OIDC middleware provides a complete OIDC authentication solution with features like:
The middleware has been tested with Auth0 and Logto, but should work with any standard OIDC provider.
This middleware follows closely the current Traefik helm chart versions. If the plugin fails to load, it's time to update to the latest version of the Traefik helm chart.
# traefik.ymlexperimental:plugins:traefikoidc:moduleName: github.com/lukaszraczylo/traefikoidcversion: v0.2.1 # Use the latest version
For local development or testing, you can use the provided Docker Compose setup:
cd dockerdocker-compose up -d
This will start Traefik with the OIDC middleware and two test services.
The middleware supports the following configuration options:
Parameter | Description | Example |
---|---|---|
providerURL | The base URL of the OIDC provider | https://accounts.google.com |
clientID | The OAuth 2.0 client identifier | 1234567890.apps.googleusercontent.com |
clientSecret | The OAuth 2.0 client secret | your-client-secret |
sessionEncryptionKey | Key used to encrypt session data (must be at least 32 bytes long) | potato-secret-is-at-least-32-bytes-long |
callbackURL | The path where the OIDC provider will redirect after authentication | /oauth2/callback |
Parameter | Description | Default | Example |
---|---|---|---|
logoutURL | The path for handling logout requests | callbackURL + "/logout" | /oauth2/logout |
postLogoutRedirectURI | The URL to redirect to after logout | / | /logged-out-page |
scopes | The OAuth 2.0 scopes to request | ["openid", "profile", "email"] | ["openid", "email", "profile", "roles"] |
logLevel | Sets the logging verbosity | info | debug , info , error |
forceHTTPS | Forces the use of HTTPS for all URLs | true | true , false |
rateLimit | Sets the maximum number of requests per second | 100 | 500 |
excludedURLs | Lists paths that bypass authentication | ["/favicon"] | ["/health", "/metrics", "/public"] |
allowedUserDomains | Restricts access to specific email domains | none | ["company.com", "subsidiary.com"] |
allowedRolesAndGroups | Restricts access to users with specific roles or groups | none | ["admin", "developer"] |
revocationURL | The endpoint for revoking tokens | auto-discovered | https://accounts.google.com/revoke |
oidcEndSessionURL | The provider's end session endpoint | auto-discovered | https://accounts.google.com/logout |
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-basicnamespace: traefikspec:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutscopes:- openid- profile
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-with-open-urlsnamespace: traefikspec:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutscopes:- openid- profileexcludedURLs:- /login # covers /login, /login/me, /login/reminder etc.- /public-data- /health- /metrics
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-domain-restrictednamespace: traefikspec:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutscopes:- openid- profileallowedUserDomains:- company.com- subsidiary.com
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-rbacnamespace: traefikspec:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutscopes:- openid- profile- roles # Include this to get role information from the providerallowedRolesAndGroups:- admin- developer
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-custom-settingsnamespace: traefikspec:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutlogLevel: debug # Options: debug, info, error (default: info)rateLimit: 500 # Requests per second (default: 100)forceHTTPS: false # Default is true for securityscopes:- openid- profile
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-custom-logoutnamespace: traefikspec:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutpostLogoutRedirectURI: /logged-out-page # Where to redirect after logoutscopes:- openid- profile
For Kubernetes environments, you can reference secrets instead of hardcoding sensitive values:
apiVersion: traefik.io/v1alpha1kind: Middlewaremetadata:name: oidc-with-secretsnamespace: traefikspec:plugin:traefikoidc:providerURL: urn:k8s:secret:traefik-middleware-oidc:ISSUERclientID: urn:k8s:secret:traefik-middleware-oidc:CLIENT_IDclientSecret: urn:k8s:secret:traefik-middleware-oidc:SECRETsessionEncryptionKey: potato-secret-is-at-least-32-bytes-longcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutscopes:- openid- profile
Don't forget to create the secret:
kubectl create secret generic traefik-middleware-oidc \--from-literal=ISSUER=https://accounts.google.com \--from-literal=CLIENT_ID=1234567890.apps.googleusercontent.com \--from-literal=SECRET=your-client-secret \-n traefik
Here's a complete example of using the middleware with Docker Compose:
version: "3.7"services:traefik:image: traefik:v3.2.1command:- "--experimental.plugins.traefikoidc.modulename=github.com/lukaszraczylo/traefikoidc"- "--experimental.plugins.traefikoidc.version=v0.2.1"volumes:- /var/run/docker.sock:/var/run/docker.sock- ./traefik-config/traefik.yml:/etc/traefik/traefik.yml- ./traefik-config/dynamic-configuration.yml:/etc/traefik/dynamic-configuration.ymllabels:- "traefik.http.routers.dash.rule=Host(`dash.localhost`)"- "traefik.http.routers.dash.service=api@internal"ports:- "80:80"hello:image: containous/whoamilabels:- traefik.enable=true- traefik.http.routers.hello.entrypoints=http- traefik.http.routers.hello.rule=Host(`hello.localhost`)- traefik.http.services.hello.loadbalancer.server.port=80- traefik.http.routers.hello.middlewares=my-plugin@filewhoami:image: jwilder/whoamilabels:- traefik.enable=true- traefik.http.routers.whoami.entrypoints=http- traefik.http.routers.whoami.rule=Host(`whoami.localhost`)- traefik.http.services.whoami.loadbalancer.server.port=8000- traefik.http.routers.whoami.middlewares=my-plugin@file
traefik-config/traefik.yml
:
log:level: INFOexperimental:localPlugins:traefikoidc:moduleName: github.com/lukaszraczylo/traefikoidc# API and dashboard configurationapi:dashboard: trueinsecure: trueentryPoints:http:address: ":80"forwardedHeaders:insecure: trueproviders:docker:endpoint: "unix:///var/run/docker.sock"exposedByDefault: falsefile:filename: /etc/traefik/dynamic-configuration.yml
traefik-config/dynamic-configuration.yml
:
http:middlewares:my-plugin:plugin:traefikoidc:providerURL: https://accounts.google.comclientID: 1234567890.apps.googleusercontent.comclientSecret: your-client-secretcallbackURL: /oauth2/callbacklogoutURL: /oauth2/logoutpostLogoutRedirectURI: /logged-out-pagesessionEncryptionKey: potato-secret-is-at-least-32-bytes-longscopes:- openid- profileallowedUserDomains:- company.comallowedRolesAndGroups:- admin- developerforceHTTPS: falselogLevel: debugrateLimit: 100excludedURLs:- /login- /public- /health- /metrics
The middleware uses encrypted cookies to manage user sessions. The sessionEncryptionKey
must be at least 32 bytes long and should be kept secret.
The middleware automatically caches validated tokens to improve performance and maintains a blacklist of revoked tokens.
When a user is authenticated, the middleware sets the following headers for downstream services:
X-Forwarded-User
: The user's email addressX-User-Groups
: Comma-separated list of user groups (if available)X-User-Roles
: Comma-separated list of user roles (if available)X-Auth-Request-Redirect
: The original request URIX-Auth-Request-User
: The user's email addressX-Auth-Request-Token
: The user's access tokenThe middleware also sets the following security headers:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Set the logLevel
to debug
to get more detailed logs:
logLevel: debug
providerURL
is correct and accessible.sessionEncryptionKey
is at least 32 bytes long.allowedUserDomains
list.allowedRolesAndGroups
.Contributions are welcome! Please feel free to submit a Pull Request.