diff --git a/.traefik.yml b/.traefik.yml index 5383001..b0119c1 100644 --- a/.traefik.yml +++ b/.traefik.yml @@ -4,28 +4,229 @@ type: middleware import: github.com/lukaszraczylo/traefikoidc summary: | - Middleware adding OIDC authentication to traefik routes. Does what it says on the tin. - Middleware has been tested with Auth0 and Logto. It should work with any OIDC provider. + Middleware adding OpenID Connect (OIDC) authentication to Traefik routes. + + This middleware replaces the need for forward-auth and oauth2-proxy when using Traefik as a reverse proxy. + It provides a complete OIDC authentication solution with features like domain restrictions, + role-based access control, token caching, and more. + + The middleware has been tested with Auth0, Logto, Google, and other standard OIDC providers. + It supports various authentication scenarios including: + + - Basic authentication with customizable callback and logout URLs + - Email domain restrictions to limit access to specific organizations + - Role and group-based access control + - Public URLs that bypass authentication + - Rate limiting to prevent brute force attacks + - Custom post-logout redirect behavior + - Secure session management with encrypted cookies + - Automatic token validation and refresh testData: - providerURL: https://accounts.google.com - clientID: 1234567890.apps.googleusercontent.com - clientSecret: secret - callbackURL: /oauth2/callback - logoutURL: /oauth2/logout - postLogoutRedirectURI: /oidc/different-logout # If not provided it will redirect to the "/" URL - scopes: # If not provided, default scopes will be used (openid, email, profile) + # Required parameters + providerURL: https://accounts.google.com # Base URL of the OIDC provider + clientID: 1234567890.apps.googleusercontent.com # OAuth 2.0 client identifier + clientSecret: secret # OAuth 2.0 client secret + callbackURL: /oauth2/callback # Path where the OIDC provider will redirect after authentication + sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long # Key used to encrypt session data (must be at least 32 bytes) + + # Optional parameters with defaults + 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 - allowedUserDomains: # If not provided - will rely entirely on the OIDC yes/no - - raczylo.com - allowedRolesAndGroups: + - roles # Include this to get role information from the provider + + allowedUserDomains: # Restricts access to specific email domains (if not provided, relies on OIDC provider) + - company.com + - subsidiary.com + + allowedRolesAndGroups: # Restricts access to users with specific roles or groups (if not provided, no role/group restrictions) - guest-endpoints - sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long - forceHTTPS: false - logLevel: debug # debug, info, warn, error - rateLimit: 100 # Simple rate limiter to prevent brute force attacks - excludedURLs: # Determines the list of URLs which are NOT a subject to authentication + - admin + - developer + + forceHTTPS: false # Forces the use of HTTPS for all URLs (default: true for security) + logLevel: debug # Sets logging verbosity: debug, info, error (default: info) + rateLimit: 100 # Maximum number of requests per second (default: 100, minimum: 10) + + excludedURLs: # Lists paths that bypass authentication - /login # covers /login, /login/me, /login/reminder etc. - - /my-public-data + - /public + - /health + - /metrics + + # Advanced parameters (usually discovered automatically from provider metadata) + revocationURL: https://accounts.google.com/revoke # Endpoint for revoking tokens + oidcEndSessionURL: https://accounts.google.com/logout # Provider's end session endpoint + +# Configuration documentation +configuration: + providerURL: + type: string + description: | + The base URL of the OIDC provider. This is the issuer URL that will be used to discover + OIDC endpoints like authorization, token, and JWKS URIs. + + Examples: + - https://accounts.google.com + - https://login.microsoftonline.com/tenant-id/v2.0 + - https://your-auth0-domain.auth0.com + - https://your-logto-instance.com/oidc + required: true + + clientID: + type: string + description: | + The OAuth 2.0 client identifier obtained from your OIDC provider. + This is the public identifier for your application. + required: true + + clientSecret: + type: string + description: | + The OAuth 2.0 client secret obtained from your OIDC provider. + This should be kept confidential and not exposed in client-side code. + + For Kubernetes deployments, you can use the secret reference format: + urn:k8s:secret:namespace:secret-name:key + required: true + + callbackURL: + type: string + description: | + The path where the OIDC provider will redirect after authentication. + This must match one of the redirect URIs configured in your OIDC provider. + + The full redirect URI will be constructed as: + [scheme]://[host][callbackURL] + + Example: /oauth2/callback + required: true + + sessionEncryptionKey: + type: string + description: | + Key used to encrypt session data stored in cookies. + Must be at least 32 bytes long for security. + + Example: potato-secret-is-at-least-32-bytes-long + required: true + + logoutURL: + type: string + description: | + The path for handling logout requests. + If not provided, it will be set to callbackURL + "/logout". + + Example: /oauth2/logout + required: false + + postLogoutRedirectURI: + type: string + description: | + The URL to redirect to after logout. + Default: "/" + + Example: /logged-out-page + required: false + + scopes: + type: array + description: | + The OAuth 2.0 scopes to request from the OIDC provider. + Default: ["openid", "profile", "email"] + + Include "roles" or similar scope if you need role/group information. + required: false + items: + type: string + + logLevel: + type: string + description: | + Sets the logging verbosity. + Valid values: "debug", "info", "error" + Default: "info" + required: false + enum: + - debug + - info + - error + + forceHTTPS: + type: boolean + description: | + Forces the use of HTTPS for all URLs. + This is recommended for security in production environments. + Default: true + required: false + + rateLimit: + type: integer + description: | + Sets the maximum number of requests per second. + This helps prevent brute force attacks. + Default: 100 + Minimum: 10 + required: false + + excludedURLs: + type: array + description: | + Lists paths that bypass authentication. + These paths will be accessible without OIDC authentication. + + The middleware uses prefix matching, so "/public" will match + "/public", "/public/page", "/public-data", etc. + + Examples: ["/health", "/metrics", "/public"] + required: false + items: + type: string + + allowedUserDomains: + type: array + description: | + Restricts access to users with email addresses from specific domains. + If not provided, the middleware relies entirely on the OIDC provider + for authentication decisions. + + Examples: ["company.com", "subsidiary.com"] + required: false + items: + type: string + + allowedRolesAndGroups: + type: array + description: | + Restricts access to users with specific roles or groups. + If not provided, no role/group restrictions are applied. + + The middleware checks both the "roles" and "groups" claims in the ID token. + + Examples: ["admin", "developer"] + required: false + items: + type: string + + revocationURL: + type: string + description: | + The endpoint for revoking tokens. + If not provided, it will be discovered from provider metadata. + + Example: https://accounts.google.com/revoke + required: false + + oidcEndSessionURL: + type: string + description: | + The provider's end session endpoint. + If not provided, it will be discovered from provider metadata. + + Example: https://accounts.google.com/logout + required: false diff --git a/README.md b/README.md index 6c296d1..7678eb5 100644 --- a/README.md +++ b/README.md @@ -1,153 +1,283 @@ -## Traefik OIDC middleware +# Traefik OIDC Middleware -This middleware is supposed to replace the need for the forward-auth and oauth2-proxy when using traefik as a reverse proxy to support the OIDC authentication. +This middleware replaces the need for forward-auth and oauth2-proxy when using Traefik as a reverse proxy to support OpenID Connect (OIDC) authentication. -Middleware has been tested with Auth0 and Logto. +## Overview -### Traefik version compatibility +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) -Code follows closely the current traefik helm chart versions. If plugin fails to load - it's time to update to the latest version of the traefik helm chart. +The middleware has been tested with Auth0 and Logto, but should work with any standard OIDC provider. -### Configuration options +## Traefik Version Compatibility -Middleware currently supports following scenarios: +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. -* Setting custom callback and logout URLs via `callbackURL` and `logoutURL` -* Allowing for access only from the listed domains if `allowedUserDomains` is set, otherwise it relies entirely on the OIDC provider -* Using excluded URLs which do **NOT** require the OIDC authentication -* Rate limiting requests to prevent the bruteforce attacks +## Installation -#### How to configure... +### As a Traefik Plugin -* `sessionEncryptionKey` should be at least 32 bytes long. - -##### Keeping secrets secret - -This works ONLY in kubernetes environments. Don't forget to create secret traefik-middleware-oidc with fields ISSUER, CLIENT_ID and SECRET keys. +1. Enable the plugin in your Traefik static configuration: +```yaml +# traefik.yml +experimental: + plugins: + traefikoidc: + moduleName: github.com/lukaszraczylo/traefikoidc + version: v0.2.1 # Use the latest version ``` + +2. 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: + +```bash +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 | `["/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` | + +## Usage Examples + +### Basic Configuration + +```yaml +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) + +```yaml 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 + +```yaml +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 + +```yaml +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 + +```yaml +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 + +```yaml +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 +``` + +### Keeping Secrets Secret in Kubernetes + +For Kubernetes environments, you can reference secrets instead of hardcoding sensitive values: + +```yaml +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: vvv - callbackURL: /cool-oidc/callback - logoutURL: /cool-oidc/logout - postLogoutRedirectURI: /my-website/you-have-logged-out # Optional post logout URL redirection + sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long + callbackURL: /oauth2/callback + logoutURL: /oauth2/logout scopes: - openid - email - profile - excludedURLs: # Determines the list of URLs which are NOT a subject to authentication - - /login # covers /login, /login/me, /login/reminder etc. - - /my-public-data ``` -##### Excluded URLs with open access +Don't forget to create the secret: -``` -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: oidc-with-open-urls - namespace: traefik -spec: - plugin: - traefikoidc: - providerURL: xxx - clientID: yyy - clientSecret: zzz - sessionEncryptionKey: vvv - callbackURL: /cool-oidc/callback - logoutURL: /cool-oidc/logout - scopes: - - openid - - email - - profile - excludedURLs: # Determines the list of URLs which are NOT a subject to authentication - - /login # covers /login, /login/me, /login/reminder etc. - - /my-public-data +```bash +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 -##### Allowed email domains - -Assuming that your OIDC provider allows anyone to log in, you may want to limit the access to people using emains in specific domain. - -``` -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: oidc-only-my-users - namespace: traefik -spec: - plugin: - traefikoidc: - providerURL: xxx - clientID: yyy - clientSecret: zzz - sessionEncryptionKey: vvv - callbackURL: /new-oidc/callback - logoutURL: /new-oidc/logout - scopes: - - openid - - email - - profile - allowedUserDomains: - - raczylo.com -``` - - -##### Allowed groups and roles - -In case of multiple roles / groups and access separation for various endpoints you will need to create multiple traefik middlewares. -Following example allows access for users who have additional role `guest-endpoints` assigned. - -``` -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: oidc-guest-endpoints - namespace: traefik -spec: - plugin: - traefikoidc: - providerURL: xxx - clientID: yyy - clientSecret: zzz - sessionEncryptionKey: vvv - callbackURL: /my-oidc/callback - logoutURL: /my-oidc/logout - scopes: - - openid - - email - - profile - - roles # This line queries the OIDC provider for roles - forceHTTPS: true - allowedRolesAndGroups: - - guest-endpoints # This line specifies the roles or groups allowed to access content - allowedUserDomains: - - raczylo.com -``` - - -#### Docker compose example - -`docker-compose.yaml` +Here's a complete example of using the middleware with Docker Compose: ```yaml version: "3.7" services: traefik: - image: traefik:v3.0.1 + image: traefik:v3.2.1 command: - "--experimental.plugins.traefikoidc.modulename=github.com/lukaszraczylo/traefikoidc" - "--experimental.plugins.traefikoidc.version=v0.2.1" @@ -158,7 +288,6 @@ services: labels: - "traefik.http.routers.dash.rule=Host(`dash.localhost`)" - "traefik.http.routers.dash.service=api@internal" - ports: - "80:80" @@ -181,8 +310,7 @@ services: - traefik.http.routers.whoami.middlewares=my-plugin@file ``` -`traefik-config/traefik.yaml` - +`traefik-config/traefik.yml`: ```yaml log: level: INFO @@ -211,7 +339,7 @@ providers: filename: /etc/traefik/dynamic-configuration.yml ``` -`traefik-config/dynamic-configuration.yaml` +`traefik-config/dynamic-configuration.yml`: ```yaml http: middlewares: @@ -220,20 +348,78 @@ http: traefikoidc: providerURL: https://accounts.google.com clientID: 1234567890.apps.googleusercontent.com - clientSecret: secret + clientSecret: your-client-secret callbackURL: /oauth2/callback logoutURL: /oauth2/logout - scopes: # If not provided, default scopes will be used (openid, email, profile) + postLogoutRedirectURI: /logged-out-page + sessionEncryptionKey: potato-secret-is-at-least-32-bytes-long + scopes: - openid - email - profile - allowedUserDomains: # If not provided - will rely entirely on the OIDC yes/no - - raczylo.com - sessionEncryptionKey: potato-secret + allowedUserDomains: + - company.com + allowedRolesAndGroups: + - admin + - developer forceHTTPS: false - logLevel: debug # debug, info, warn, error - rateLimit: 100 # Simple rate limiter to prevent brute force attacks - excludedURLs: # Determines the list of URLs which are NOT a subject to authentication - - /login # covers /login, /login/me, /login/reminder etc. - - /my-public-data + logLevel: debug + rateLimit: 100 + excludedURLs: + - /login + - /public + - /health + - /metrics ``` + +## 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. + +### Token Caching and Blacklisting + +The middleware automatically caches validated tokens to improve performance and maintains a blacklist of revoked tokens. + +### 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: + +```yaml +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`. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. diff --git a/main.go b/main.go index 56f2d8f..7c7cdcc 100644 --- a/main.go +++ b/main.go @@ -116,6 +116,16 @@ var defaultExcludedURLs = map[string]struct{}{ "/favicon": {}, } +// VerifyToken implements the TokenVerifier interface to verify an OIDC token. +// It performs a complete verification process including: +// 1. Checking the token cache to avoid redundant verifications +// 2. Performing rate limiting and blacklist checks +// 3. Parsing the JWT structure +// 4. Verifying the JWT signature against the JWKS from the provider +// 5. Validating standard JWT claims (iss, aud, exp, etc.) +// 6. Caching the verified token for future requests +// +// Returns nil if the token is valid, or an error describing the validation failure. func (t *TraefikOidc) VerifyToken(token string) error { // Check cache first if claims, exists := t.tokenCache.Get(token); exists && len(claims) > 0 { @@ -221,7 +231,24 @@ func (t *TraefikOidc) VerifyJWTSignatureAndClaims(jwt *JWT, token string) error return nil } -// New creates a new instance of the OIDC middleware +// New creates a new instance of the OIDC middleware. +// This is the main entry point for the middleware and is called by Traefik when loading the plugin. +// It initializes all components needed for OIDC authentication: +// - Session management for storing user state +// - Token caching and blacklisting +// - JWK caching for signature verification +// - Rate limiting to prevent abuse +// - Metadata discovery for OIDC provider endpoints +// +// Parameters: +// - ctx: Context for initialization operations +// - next: The next handler in the middleware chain +// - config: Configuration options for the middleware +// - name: Identifier for this middleware instance +// +// Returns: +// - An http.Handler that implements the middleware +// - An error if initialization fails func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { if config == nil { config = CreateConfig() @@ -425,7 +452,18 @@ func fetchMetadata(wellKnownURL string, httpClient *http.Client) (*ProviderMetad return &metadata, nil } -// ServeHTTP is the main handler for the middleware +// ServeHTTP is the main handler for the middleware that processes all HTTP requests. +// It implements the http.Handler interface and performs the following operations: +// 1. Waits for OIDC provider metadata initialization to complete +// 2. Checks if the requested URL is in the excluded list (bypassing authentication) +// 3. Retrieves or creates a user session +// 4. Handles special paths like callback and logout URLs +// 5. Verifies authentication status and token validity +// 6. Refreshes tokens that are about to expire +// 7. Validates user email domains, roles, and groups against configured restrictions +// 8. Sets appropriate headers for downstream services +// 9. Applies security headers to responses +// 10. Forwards the authenticated request to the next handler func (t *TraefikOidc) ServeHTTP(rw http.ResponseWriter, req *http.Request) { select { case <-t.initComplete: @@ -614,7 +652,17 @@ func (t *TraefikOidc) determineHost(req *http.Request) string { return req.Host } -// isUserAuthenticated checks if the user is authenticated +// isUserAuthenticated checks if the user is authenticated by validating their session and token. +// It performs a comprehensive check of the authentication state including: +// 1. Verifying the session's authenticated flag +// 2. Checking for the presence of an access token +// 3. Validating the token's signature and claims +// 4. Checking the token's expiration time +// +// Returns three boolean values: +// - authenticated: Whether the user is currently authenticated +// - needsRefresh: Whether the token is valid but will expire soon (within grace period) +// - expired: Whether the token has expired or is otherwise invalid func (t *TraefikOidc) isUserAuthenticated(session *SessionData) (bool, bool, bool) { if !session.GetAuthenticated() { t.logger.Debug("User is not authenticated according to session") @@ -662,7 +710,19 @@ func (t *TraefikOidc) isUserAuthenticated(session *SessionData) (bool, bool, boo return true, false, false } -// defaultInitiateAuthentication initiates the authentication process +// defaultInitiateAuthentication initiates the OIDC authentication process. +// This function prepares and starts a new authentication flow by: +// 1. Generating security tokens (CSRF token and nonce) to prevent attacks +// 2. Clearing any existing session data to avoid state conflicts +// 3. Storing the original request path to redirect back after authentication +// 4. Building the authorization URL with all required OIDC parameters +// 5. Redirecting the user to the OIDC provider's authorization endpoint +// +// Parameters: +// - rw: The HTTP response writer for sending the redirect +// - req: The original HTTP request that triggered authentication +// - session: The user's session data for storing authentication state +// - redirectURL: The callback URL where the OIDC provider will redirect after authentication func (t *TraefikOidc) defaultInitiateAuthentication(rw http.ResponseWriter, req *http.Request, session *SessionData, redirectURL string) { // Generate CSRF token and nonce csrfToken := uuid.NewString() @@ -692,7 +752,10 @@ func (t *TraefikOidc) defaultInitiateAuthentication(rw http.ResponseWriter, req http.Redirect(rw, req, authURL, http.StatusFound) } -// verifyToken verifies the token using the token verifier +// verifyToken verifies the token using the token verifier interface. +// This function delegates to the configured token verifier implementation, +// which by default is the TraefikOidc instance itself (implementing the VerifyToken method). +// This design allows for easy mocking in tests and potential future extension. func (t *TraefikOidc) verifyToken(token string) error { return t.tokenVerifier.VerifyToken(token) }