This container acts as a ForwardAuth middleware for the traefik ingress controller that provides OpenID Connect (OIDC) authorization.
- Blazing fast ⚡️ and written in Rust ⚙️
- Secure implementation 🔐 with PKCE, CSRF protection, and SameSite cookies
- Integration with traefik ingress controller
- Easy to deploy to a Kubernetes environment via Helm or to use it with Docker Compose
- Simple configuration via environment variables or Helm values
- Optional server-side session store with Redis/Valkey for improved token management
- Session Store: Optional server-side session storage using Redis/Valkey. This is essential for OIDC providers that use single-use refresh tokens (like Kanidm) to prevent race conditions when multiple concurrent requests attempt to refresh tokens.
- Distributed locking: When session store is enabled, token refresh operations use distributed locks to ensure only one request refreshes the token at a time.
- Added the HTTP header
X-Forwarded-Usercontaining the jwt user sub. Your backend can directly consume that header to get the user id without the need to encrypt or validate jwt tokens. - Helm Chart: Added support for Traefik V2 and V3 middleware CRDs
- PKCE (Proof Key for Code Exchange) for secure authorization code flow
- CSRF protection via state parameter with cryptographic nonce
- HttpOnly & Secure cookies to prevent XSS attacks
- SameSite=Lax cookies for additional CSRF protection
- Automatic token expiry based on JWT
expclaim - Redirect loop prevention on failed authentication callbacks
Let's say you have a service called app.example.com you would like to protect with the OIDC middleware. Your OIDC provider in this example is id.example.com.
First, install the oidc-forward-auth-middleware Helm chart:
helm install oci://ghcr.io/espresso-lab/helm-charts/oidc-forward-auth-middleware
The Helm values could look like the following:
# Example helm values of oidc-forward-auth-middleware
config:
logLevel: info # debug, info, warn, error
oidcProviders:
- ingressHostname: app.example.com # Traefik ingress hostname you would like to protect
issuerUrl: https://id.example.com/oauth/app1
clientId: app1
clientSecret: mysecretpassword
scopes: ["email", "profile"]
# existingSecret: oidc-config # Provide a secret in the same namespace with fields clientId, clientSecret
audience: ["app1"]If your OIDC provider uses single-use refresh tokens (e.g., Kanidm), enable the session store to prevent race conditions:
# Enable built-in Valkey instance
sessionStore:
enabled: true
# Or use an external Redis/Valkey instance
sessionStore:
enabled: true
externalUrl: "redis://your-redis-host:6379"Last, enable it in the ingress controller of the service app.example.com you would like to protect:
# Example helm values of service app.example.com
ingress:
enabled: true
hosts:
- app.example.com
ingressClassName: traefik
annotations:
# Enable middleware
traefik.ingress.kubernetes.io/router.middlewares: kube-system-oidc-forward-auth-middleware@kubernetescrdFor the latest example have a look at the docker-compose.yml file.
| Environment variable | Type | Description |
|---|---|---|
| RUST_LOG | String | info, debug, error, warning |
| BIND_ADDRESS | String | Default: 0.0.0.0:3000. The network address to bind to. |
| DISABLE_ENHANCED_SECURITY | Boolean | Default: false. Sets various security HTTP headers and redirects http requests to https. |
| REDIS_URL | String | Optional. Redis/Valkey connection URL for session store. Example: redis://localhost:6379 |
Use OIDC_PROVIDER_0_* for the first provider, OIDC_PROVIDER_1_* for the second one and so on.
| Environment variable | Type | Description | Example |
|---|---|---|---|
| OIDC_PROVIDER_0_HOSTNAME | String | Name of the hostname (traefik ingress host) | app.example.com |
| OIDC_PROVIDER_0_ISSUER_URL | String | OIDC issuer url | https://id.example.com/oauth2/openid/app |
| OIDC_PROVIDER_0_CLIENT_ID | String | OIDC client id | my-app-client |
| OIDC_PROVIDER_0_CLIENT_SECRET | String | OIDC client secret | super-secret-password |
| OIDC_PROVIDER_0_SCOPES | String | OIDC scopes (openid, email, ...) | openid,email,profile |
| OIDC_PROVIDER_0_AUDIENCE | String | OIDC audience | my-app-client |
The middleware sets the following cookies:
Without session store (default):
| Cookie | Purpose | Expiry |
|---|---|---|
x_oidc_access_token |
JWT access token | Based on JWT exp claim |
x_oidc_refresh_token |
Refresh token for silent renewal | Session |
x_oidc_csrf_{nonce} |
CSRF protection during auth flow | 1 hour |
x_oidc_pkce_{nonce} |
PKCE verifier during auth flow | 1 hour |
With session store enabled:
| Cookie | Purpose | Expiry |
|---|---|---|
x_oidc_session |
Session ID (tokens stored server-side) | 24 hours |
x_oidc_csrf_{nonce} |
CSRF protection during auth flow | 1 hour |
x_oidc_pkce_{nonce} |
PKCE verifier during auth flow | 1 hour |
Released under MIT by @espresso-lab.
