mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
bde1db1c3b
* Automatic discovery of the scopes. Issue #61 raised very valid concerns about users configuring scopes that are not supported by the provider. This change introduces automatic discovery of supported scopes by fetching the provider's discovery document and filtering out unsupported scopes. Before: User configures: scopes: ["openid", "profile", "email", "offline_access"] Self-hosted GitLab: "The requested scope is invalid, unknown, or malformed" Authentication: ❌ FAILS After: User configures: scopes: ["openid", "profile", "email", "offline_access"] Middleware checks discovery doc → offline_access not supported Automatically filters to: ["openid", "profile", "email"] Authentication: ✅ SUCCEEDS * Resolves issue #74 by enabling user to specify expected audience in the configuration. * Fix flaky tests.
955 lines
29 KiB
Markdown
955 lines
29 KiB
Markdown
# Provider-Specific Configuration Guide
|
|
|
|
This guide covers the configuration requirements and best practices for each supported OIDC provider.
|
|
|
|
## Table of Contents
|
|
|
|
- [Google](#google)
|
|
- [Microsoft Azure AD](#microsoft-azure-ad)
|
|
- [Auth0](#auth0)
|
|
- [GitHub](#github)
|
|
- [GitLab](#gitlab)
|
|
- [AWS Cognito](#aws-cognito)
|
|
- [Keycloak](#keycloak)
|
|
- [Okta](#okta)
|
|
- [Generic OIDC](#generic-oidc)
|
|
|
|
---
|
|
|
|
## Google
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://accounts.google.com"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-google-client-id.apps.googleusercontent.com"
|
|
clientSecret: "your-google-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email"]
|
|
```
|
|
|
|
### Google-Specific Features
|
|
- **Automatic offline access**: Google provider automatically adds `access_type=offline` and `prompt=consent`
|
|
- **Scope filtering**: Automatically removes `offline_access` scope (not used by Google)
|
|
- **Refresh token support**: Fully supported
|
|
- **Domain restrictions**: Can restrict by Google Workspace domains
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
# Traefik dynamic configuration
|
|
http:
|
|
middlewares:
|
|
google-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://accounts.google.com"
|
|
clientId: "123456789-abcdef.apps.googleusercontent.com"
|
|
clientSecret: "GOCSPX-your-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["openid", "profile", "email"]
|
|
allowedUserDomains: ["example.com", "company.org"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
### Google OAuth Console Setup
|
|
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
2. Create or select a project
|
|
3. Enable Google+ API
|
|
4. Create OAuth 2.0 credentials
|
|
5. Add authorized redirect URIs: `https://your-domain.com/auth/callback`
|
|
|
|
---
|
|
|
|
## Microsoft Azure AD
|
|
|
|
### Provider URL
|
|
```yaml
|
|
# For Azure AD (single tenant)
|
|
providerUrl: "https://login.microsoftonline.com/{tenant-id}/v2.0"
|
|
|
|
# For Azure AD (multi-tenant)
|
|
providerUrl: "https://login.microsoftonline.com/common/v2.0"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-azure-application-id"
|
|
clientSecret: "your-azure-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
```
|
|
|
|
### Azure-Specific Features
|
|
- **Response mode**: Automatically adds `response_mode=query`
|
|
- **Offline access**: Requires `offline_access` scope for refresh tokens
|
|
- **Access token validation**: Supports both JWT and opaque access tokens
|
|
- **Tenant isolation**: Can restrict to specific Azure AD tenants
|
|
- **Application ID URI**: Supports custom audience for protected APIs
|
|
|
|
### Example Configuration (Basic)
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
azure-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://login.microsoftonline.com/common/v2.0"
|
|
clientId: "12345678-1234-1234-1234-123456789abc"
|
|
clientSecret: "your-azure-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
postLogoutRedirectUri: "https://app.example.com"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
allowedRolesAndGroups: ["App.Users", "Admin.Group"]
|
|
forceHttps: true
|
|
```
|
|
|
|
### Azure AD API Configuration (Application ID URI)
|
|
|
|
When exposing your application as an API with a custom Application ID URI, you need to specify the `audience` parameter. Azure AD includes the Application ID URI in the JWT `aud` claim.
|
|
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
azure-api-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://login.microsoftonline.com/common/v2.0"
|
|
clientId: "12345678-1234-1234-1234-123456789abc"
|
|
clientSecret: "your-azure-client-secret"
|
|
# Specify the Application ID URI as audience
|
|
audience: "api://12345678-1234-1234-1234-123456789abc"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
forceHttps: true
|
|
```
|
|
|
|
**Important**:
|
|
- The `audience` parameter should match your Application ID URI (typically `api://{app-id}`)
|
|
- Find your Application ID URI in Azure Portal → App Registration → Expose an API → Application ID URI
|
|
- Without the `audience` parameter, access tokens with custom audiences will be rejected
|
|
- For ID token validation only (no API access), you can omit the `audience` parameter
|
|
|
|
### Azure App Registration Setup
|
|
1. Go to [Azure Portal](https://portal.azure.com/)
|
|
2. Navigate to "Azure Active Directory" > "App registrations"
|
|
3. Create new registration
|
|
4. Add redirect URI: `https://your-domain.com/auth/callback`
|
|
5. Create client secret in "Certificates & secrets"
|
|
6. Configure API permissions for required scopes
|
|
|
|
### Azure AD API Exposure Setup (for custom audiences)
|
|
1. In your App Registration, go to "Expose an API"
|
|
2. Set the Application ID URI (e.g., `api://12345678-1234-1234-1234-123456789abc`)
|
|
3. Add any custom scopes your API exposes
|
|
4. Update the middleware configuration to include the `audience` parameter with this URI
|
|
|
|
---
|
|
|
|
## Auth0
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://your-domain.auth0.com"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-auth0-client-id"
|
|
clientSecret: "your-auth0-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
```
|
|
|
|
### Auth0-Specific Features
|
|
- **Custom domains**: Supports Auth0 custom domains
|
|
- **Rules and hooks**: Leverages Auth0's extensibility
|
|
- **Social connections**: Works with Auth0's social identity providers
|
|
- **Offline access**: Requires `offline_access` scope
|
|
- **API audiences**: Supports custom audience for API access tokens
|
|
|
|
### Example Configuration (Basic)
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
auth0-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://company.auth0.com"
|
|
clientId: "abcdef123456789"
|
|
clientSecret: "your-auth0-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
postLogoutRedirectUri: "https://app.example.com"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
allowedUsers: ["user@example.com", "admin@company.com"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
### Auth0 API Configuration (Custom Audience)
|
|
|
|
When using Auth0 APIs with custom audience parameters, you need to specify the `audience` field. Auth0 includes the API identifier in the JWT `aud` claim instead of the `clientId`.
|
|
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
auth0-api-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://company.auth0.com"
|
|
clientId: "abcdef123456789"
|
|
clientSecret: "your-auth0-client-secret"
|
|
# Specify the Auth0 API identifier as audience
|
|
audience: "https://api.company.com"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
**Important**:
|
|
- The `audience` parameter should match your Auth0 API identifier (not the client ID)
|
|
- Find your API identifier in Auth0 Dashboard → APIs → Your API → Settings → Identifier
|
|
- Without the `audience` parameter, access tokens with custom audiences will be rejected with "invalid audience" error
|
|
- For ID token validation only (no APIs), you can omit the `audience` parameter
|
|
|
|
### Auth0 Application Setup
|
|
1. Go to [Auth0 Dashboard](https://manage.auth0.com/)
|
|
2. Create new application (Regular Web Application)
|
|
3. Configure allowed callback URLs: `https://your-domain.com/auth/callback`
|
|
4. Configure allowed logout URLs: `https://your-domain.com/auth/logout`
|
|
5. Enable OIDC Conformant in Advanced Settings
|
|
|
|
### Auth0 API Setup (for custom audiences)
|
|
1. Go to Auth0 Dashboard → APIs
|
|
2. Create a new API or select existing API
|
|
3. Note the "Identifier" field (e.g., `https://api.company.com`) - this is your `audience` value
|
|
4. In API Settings → Machine to Machine Applications, authorize your application
|
|
5. Configure API permissions/scopes as needed
|
|
6. Use the API identifier as the `audience` parameter in your configuration
|
|
|
|
---
|
|
|
|
## GitHub
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://github.com"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-github-client-id"
|
|
clientSecret: "your-github-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["read:user", "user:email"]
|
|
```
|
|
|
|
### GitHub-Specific Features
|
|
- **Organization membership**: Can restrict by GitHub organization
|
|
- **Team membership**: Can restrict by specific teams
|
|
- **Limited OIDC**: GitHub has limited OIDC support
|
|
- **Email verification**: Requires verified email addresses
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
github-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://github.com"
|
|
clientId: "Iv1.abcdef123456"
|
|
clientSecret: "your-github-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["read:user", "user:email"]
|
|
allowedUsers: ["octocat", "github-user"]
|
|
forceHttps: true
|
|
```
|
|
|
|
### GitHub OAuth App Setup
|
|
1. Go to GitHub Settings > Developer settings > OAuth Apps
|
|
2. Create new OAuth App
|
|
3. Set Authorization callback URL: `https://your-domain.com/auth/callback`
|
|
4. Note the Client ID and generate Client Secret
|
|
|
|
---
|
|
|
|
## GitLab
|
|
|
|
### Provider URL
|
|
```yaml
|
|
# GitLab.com
|
|
providerUrl: "https://gitlab.com"
|
|
|
|
# Self-hosted GitLab
|
|
providerUrl: "https://gitlab.your-company.com"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-gitlab-application-id"
|
|
clientSecret: "your-gitlab-application-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email"]
|
|
```
|
|
|
|
### GitLab-Specific Features
|
|
- **Self-hosted support**: Works with self-hosted GitLab instances
|
|
- **Group membership**: Can restrict by GitLab groups
|
|
- **Project access**: Can validate project permissions
|
|
- **Offline access**: Supports refresh tokens without requiring `offline_access` scope
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
gitlab-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://gitlab.com"
|
|
clientId: "abcdef123456789"
|
|
clientSecret: "your-gitlab-application-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["openid", "profile", "email"]
|
|
# Note: GitLab doesn't support the offline_access scope.
|
|
# Refresh tokens are issued automatically for the openid scope.
|
|
allowedRolesAndGroups: ["developers", "maintainers"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
### GitLab Application Setup
|
|
1. Go to GitLab Settings > Applications
|
|
2. Create new application
|
|
3. Add scopes: `openid`, `profile`, `email`
|
|
4. Set redirect URI: `https://your-domain.com/auth/callback`
|
|
5. Save and note the Application ID and Secret
|
|
|
|
---
|
|
|
|
## AWS Cognito
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://cognito-idp.{region}.amazonaws.com/{user-pool-id}"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-cognito-app-client-id"
|
|
clientSecret: "your-cognito-app-client-secret" # If app client has secret
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email"]
|
|
```
|
|
|
|
### Cognito-Specific Features
|
|
- **User pools**: Integrates with Cognito User Pools
|
|
- **Custom attributes**: Supports custom user attributes
|
|
- **Groups**: Can validate Cognito user group membership
|
|
- **Regional endpoints**: Requires region-specific URLs
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
cognito-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABCDEF123"
|
|
clientId: "1234567890abcdefghij"
|
|
clientSecret: "your-cognito-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["openid", "profile", "email"]
|
|
allowedRolesAndGroups: ["admin", "users"]
|
|
forceHttps: true
|
|
```
|
|
|
|
### AWS Cognito Setup
|
|
1. Create Cognito User Pool
|
|
2. Create App Client with OIDC scopes
|
|
3. Configure App Client settings:
|
|
- Callback URLs: `https://your-domain.com/auth/callback`
|
|
- Sign out URLs: `https://your-domain.com/auth/logout`
|
|
- OAuth flows: Authorization code grant
|
|
4. Configure hosted UI domain (optional)
|
|
|
|
---
|
|
|
|
## Keycloak
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://keycloak.your-company.com/realms/{realm-name}"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-keycloak-client-id"
|
|
clientSecret: "your-keycloak-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email"]
|
|
```
|
|
|
|
### Keycloak-Specific Features
|
|
- **Realm support**: Multi-realm deployments
|
|
- **Custom mappers**: Rich claim mapping capabilities
|
|
- **Role-based access**: Fine-grained role management
|
|
- **Offline access**: Full refresh token support
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
keycloak-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://keycloak.company.com/realms/employees"
|
|
clientId: "traefik-app"
|
|
clientSecret: "your-keycloak-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
postLogoutRedirectUri: "https://app.example.com"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
allowedRolesAndGroups: ["app-users", "administrators"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
### Keycloak Client Setup
|
|
1. Access Keycloak Admin Console
|
|
2. Select appropriate realm
|
|
3. Create new client:
|
|
- Client Protocol: openid-connect
|
|
- Access Type: confidential
|
|
- Valid Redirect URIs: `https://your-domain.com/auth/callback`
|
|
4. Configure client scopes and mappers
|
|
5. Generate client secret in Credentials tab
|
|
|
|
---
|
|
|
|
## Okta
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://your-domain.okta.com"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-okta-client-id"
|
|
clientSecret: "your-okta-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
```
|
|
|
|
### Okta-Specific Features
|
|
- **Custom authorization servers**: Supports custom auth servers
|
|
- **Group claims**: Rich group membership information
|
|
- **Universal Directory**: Integrates with Okta's user store
|
|
- **Offline access**: Requires `offline_access` scope
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
okta-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://company.okta.com"
|
|
clientId: "0oa123456789abcdef"
|
|
clientSecret: "your-okta-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
postLogoutRedirectUri: "https://app.example.com"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
allowedRolesAndGroups: ["Everyone", "Administrators"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
### Okta Application Setup
|
|
1. Access Okta Admin Console
|
|
2. Go to Applications > Create App Integration
|
|
3. Select OIDC - OpenID Connect
|
|
4. Choose Web Application
|
|
5. Configure:
|
|
- Sign-in redirect URIs: `https://your-domain.com/auth/callback`
|
|
- Sign-out redirect URIs: `https://your-domain.com/auth/logout`
|
|
- Grant types: Authorization Code, Refresh Token
|
|
6. Assign users or groups
|
|
|
|
---
|
|
|
|
## Generic OIDC
|
|
|
|
### Provider URL
|
|
```yaml
|
|
providerUrl: "https://your-oidc-provider.com"
|
|
```
|
|
|
|
### Required Configuration
|
|
```yaml
|
|
clientId: "your-client-id"
|
|
clientSecret: "your-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
scopes: ["openid", "profile", "email"]
|
|
```
|
|
|
|
### Generic Features
|
|
- **Standards compliance**: Works with any OIDC-compliant provider
|
|
- **Auto-discovery**: Uses `.well-known/openid-configuration` endpoint
|
|
- **Flexible scopes**: Supports custom scope requirements
|
|
- **Custom claims**: Works with provider-specific claims
|
|
|
|
### Example Configuration
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
generic-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://oidc.your-provider.com"
|
|
clientId: "your-client-id"
|
|
clientSecret: "your-client-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
logoutUrl: "https://app.example.com/auth/logout"
|
|
scopes: ["openid", "profile", "email"]
|
|
forceHttps: true
|
|
enablePkce: true
|
|
```
|
|
|
|
---
|
|
|
|
## Automatic Scope Filtering
|
|
|
|
### Overview
|
|
|
|
The middleware automatically filters OAuth scopes based on the provider's capabilities declared in their OIDC discovery document (`.well-known/openid-configuration`). This prevents authentication failures when providers reject unsupported scopes.
|
|
|
|
### How It Works
|
|
|
|
1. **Discovery Document Parsing**: The middleware fetches the provider's discovery document and extracts the `scopes_supported` field
|
|
2. **Intelligent Filtering**: Requested scopes are filtered to only include those the provider supports
|
|
3. **Fallback Behavior**: If the provider doesn't declare `scopes_supported`, all requested scopes are used (backward compatible)
|
|
4. **Provider-Specific Handling**: Special logic for Google and Azure is preserved and applied after filtering
|
|
|
|
### Example Scenarios
|
|
|
|
#### Self-Hosted GitLab
|
|
|
|
**Problem**: Self-hosted GitLab instances reject the `offline_access` scope with error:
|
|
```
|
|
The requested scope is invalid, unknown, or malformed.
|
|
```
|
|
|
|
**Solution**: The middleware automatically detects this by:
|
|
1. Reading GitLab's discovery document at `https://gitlab.example.com/.well-known/openid-configuration`
|
|
2. Observing that `offline_access` is NOT in the `scopes_supported` list
|
|
3. Filtering out `offline_access` from the request
|
|
4. Authentication succeeds
|
|
|
|
**Configuration**:
|
|
```yaml
|
|
http:
|
|
middlewares:
|
|
gitlab-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
providerUrl: "https://gitlab.example.com"
|
|
clientId: "your-gitlab-application-id"
|
|
clientSecret: "your-gitlab-application-secret"
|
|
callbackUrl: "https://app.example.com/auth/callback"
|
|
scopes: ["openid", "profile", "email", "offline_access"]
|
|
# Even though offline_access is listed, it will be automatically
|
|
# filtered out if GitLab doesn't support it
|
|
```
|
|
|
|
#### Auth0 or Keycloak
|
|
|
|
These providers typically support `offline_access` and it will be included:
|
|
|
|
```yaml
|
|
# Auth0 scopes_supported: ["openid", "profile", "email", "offline_access", ...]
|
|
# Result: All requested scopes are sent
|
|
```
|
|
|
|
### Benefits
|
|
|
|
1. **Self-Hosted Support**: Works seamlessly with self-hosted provider instances
|
|
2. **No Manual Configuration**: No need to know which scopes each provider supports
|
|
3. **Error Prevention**: Eliminates "invalid scope" authentication failures
|
|
4. **Standards Compliant**: Uses official OIDC discovery specification (RFC 8414)
|
|
5. **Backward Compatible**: Existing configurations continue to work
|
|
|
|
### Logging
|
|
|
|
The middleware provides detailed logging for scope filtering:
|
|
|
|
```
|
|
INFO: ScopeFilter: Filtered unsupported scopes for https://gitlab.example.com: [offline_access]
|
|
DEBUG: ScopeFilter: Provider https://gitlab.example.com supported scopes: [openid profile email read_user read_api]
|
|
DEBUG: ScopeFilter: Final filtered scopes: [openid profile email]
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
**Issue**: Provider rejects scope even after filtering
|
|
|
|
**Possible Causes**:
|
|
1. Provider's discovery document is outdated
|
|
2. Provider doesn't properly implement `scopes_supported`
|
|
3. Custom authorization server with non-standard behavior
|
|
|
|
**Solutions**:
|
|
1. Use `overrideScopes: true` and explicitly list only supported scopes
|
|
2. Check the provider's discovery document manually: `curl https://your-provider/.well-known/openid-configuration`
|
|
3. Review middleware debug logs for filtering decisions
|
|
|
|
---
|
|
|
|
## Common Configuration Options
|
|
|
|
### Audience Configuration
|
|
|
|
The `audience` parameter specifies the expected JWT audience claim value. This is particularly important when using Auth0 APIs, Azure AD Application ID URIs, or other providers with custom audience requirements.
|
|
|
|
```yaml
|
|
# Optional: Custom audience for JWT validation
|
|
# If not set, defaults to clientID for backward compatibility
|
|
audience: "https://api.example.com" # Auth0 API identifier
|
|
# OR
|
|
audience: "api://12345-guid" # Azure AD Application ID URI
|
|
```
|
|
|
|
**When to use**:
|
|
- **Auth0**: When using Auth0 APIs with custom audience parameters
|
|
- **Azure AD**: When exposing your app as an API with Application ID URI
|
|
- **Keycloak**: When using audience-restricted tokens
|
|
- **Okta**: When using custom authorization servers with API audiences
|
|
|
|
**When to omit**:
|
|
- For standard ID token validation (default behavior)
|
|
- When the provider sets `aud` claim to your `clientID`
|
|
- For backward compatibility with existing configurations
|
|
|
|
**Security Note**: The `audience` parameter prevents token confusion attacks by ensuring tokens issued for one service cannot be used at another service.
|
|
|
|
### Security Settings
|
|
```yaml
|
|
# Force HTTPS (recommended for production)
|
|
forceHttps: true
|
|
|
|
# Enable PKCE (recommended for security)
|
|
enablePkce: true
|
|
|
|
# Session encryption key (32+ characters)
|
|
sessionEncryptionKey: "your-very-long-encryption-key-here"
|
|
```
|
|
|
|
### Access Control
|
|
```yaml
|
|
# Restrict by email addresses
|
|
allowedUsers: ["user1@example.com", "user2@example.com"]
|
|
|
|
# Restrict by email domains
|
|
allowedUserDomains: ["company.com", "partner.org"]
|
|
|
|
# Restrict by roles/groups (provider-specific)
|
|
allowedRolesAndGroups: ["admin", "users", "developers"]
|
|
```
|
|
|
|
### URLs and Endpoints
|
|
```yaml
|
|
# OAuth callback URL (must match provider config)
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
|
|
# Logout endpoint
|
|
logoutUrl: "https://your-domain.com/auth/logout"
|
|
|
|
# Post-logout redirect (optional)
|
|
postLogoutRedirectUri: "https://your-domain.com"
|
|
|
|
# URLs to exclude from authentication
|
|
excludedUrls: ["/health", "/metrics", "/public"]
|
|
```
|
|
|
|
### Advanced Settings
|
|
```yaml
|
|
# Override default scopes
|
|
overrideScopes: true
|
|
scopes: ["openid", "custom_scope"]
|
|
|
|
# Rate limiting (requests per second)
|
|
rateLimit: 10
|
|
|
|
# Token refresh grace period (seconds)
|
|
refreshGracePeriodSeconds: 60
|
|
|
|
# Cookie domain (for subdomain sharing)
|
|
cookieDomain: ".example.com"
|
|
|
|
# Custom headers to inject
|
|
headers:
|
|
- name: "X-User-Email"
|
|
value: "{{.Claims.email}}"
|
|
- name: "X-User-Name"
|
|
value: "{{.Claims.name}}"
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Invalid redirect URI**
|
|
- Ensure callback URL exactly matches provider configuration
|
|
- Check for HTTP vs HTTPS mismatches
|
|
|
|
2. **Scope errors**
|
|
- Verify required scopes are configured in provider
|
|
- Some providers require specific scopes for refresh tokens
|
|
|
|
3. **Token validation failures**
|
|
- Check provider URL format and accessibility
|
|
- Verify `.well-known/openid-configuration` endpoint is reachable
|
|
|
|
4. **Session issues**
|
|
- Ensure session encryption key is properly configured
|
|
- Check cookie domain settings for subdomain scenarios
|
|
|
|
### Debug Mode
|
|
Enable debug logging to troubleshoot configuration issues:
|
|
```yaml
|
|
logLevel: "debug"
|
|
```
|
|
|
|
This will provide detailed logs of the authentication flow and help identify configuration problems.
|
|
|
|
---
|
|
|
|
## Security Headers Configuration
|
|
|
|
The plugin includes comprehensive security headers support to protect your applications against common web vulnerabilities.
|
|
|
|
### Default Security Headers
|
|
|
|
By default, the plugin applies these security headers:
|
|
|
|
- `X-Frame-Options: DENY` - Prevents clickjacking
|
|
- `X-Content-Type-Options: nosniff` - Prevents MIME sniffing
|
|
- `X-XSS-Protection: 1; mode=block` - Enables XSS protection
|
|
- `Referrer-Policy: strict-origin-when-cross-origin` - Controls referrer information
|
|
- `Strict-Transport-Security` - Forces HTTPS (when HTTPS is detected)
|
|
|
|
### Security Profiles
|
|
|
|
Choose from predefined security profiles or create custom configurations:
|
|
|
|
#### Default Profile (Recommended)
|
|
```yaml
|
|
securityHeaders:
|
|
enabled: true
|
|
profile: "default"
|
|
```
|
|
|
|
#### Strict Profile (Maximum Security)
|
|
```yaml
|
|
securityHeaders:
|
|
enabled: true
|
|
profile: "strict"
|
|
# Additional strict CSP and cross-origin policies
|
|
```
|
|
|
|
#### Development Profile (Local Development)
|
|
```yaml
|
|
securityHeaders:
|
|
enabled: true
|
|
profile: "development"
|
|
# Relaxed policies for local development
|
|
```
|
|
|
|
#### API Profile (API Endpoints)
|
|
```yaml
|
|
securityHeaders:
|
|
enabled: true
|
|
profile: "api"
|
|
corsEnabled: true
|
|
corsAllowedOrigins: ["https://your-frontend.com"]
|
|
```
|
|
|
|
### Custom Security Configuration
|
|
|
|
For complete control, use the custom profile:
|
|
|
|
```yaml
|
|
securityHeaders:
|
|
enabled: true
|
|
profile: "custom"
|
|
|
|
# Content Security Policy
|
|
contentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline'"
|
|
|
|
# HSTS Configuration
|
|
strictTransportSecurity: true
|
|
strictTransportSecurityMaxAge: 31536000 # 1 year
|
|
strictTransportSecuritySubdomains: true
|
|
strictTransportSecurityPreload: true
|
|
|
|
# Frame and content protection
|
|
frameOptions: "DENY" # or "SAMEORIGIN", "ALLOW-FROM uri"
|
|
contentTypeOptions: "nosniff"
|
|
xssProtection: "1; mode=block"
|
|
referrerPolicy: "strict-origin-when-cross-origin"
|
|
|
|
# Permissions policy (feature policy)
|
|
permissionsPolicy: "geolocation=(), microphone=(), camera=()"
|
|
|
|
# Cross-origin policies
|
|
crossOriginEmbedderPolicy: "require-corp"
|
|
crossOriginOpenerPolicy: "same-origin"
|
|
crossOriginResourcePolicy: "same-origin"
|
|
|
|
# CORS configuration
|
|
corsEnabled: true
|
|
corsAllowedOrigins:
|
|
- "https://app.example.com"
|
|
- "https://*.api.example.com"
|
|
corsAllowedMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
|
|
corsAllowedHeaders: ["Authorization", "Content-Type", "X-Requested-With"]
|
|
corsAllowCredentials: true
|
|
corsMaxAge: 86400 # 24 hours
|
|
|
|
# Custom headers
|
|
customHeaders:
|
|
X-Custom-Header: "custom-value"
|
|
X-API-Version: "v1"
|
|
|
|
# Server identification
|
|
disableServerHeader: true
|
|
disablePoweredByHeader: true
|
|
```
|
|
|
|
### Complete Example with Security Headers
|
|
|
|
Here's a complete configuration example for Google OIDC with custom security headers:
|
|
|
|
```yaml
|
|
# Traefik dynamic configuration
|
|
http:
|
|
middlewares:
|
|
secure-google-oidc:
|
|
plugin:
|
|
traefik-oidc:
|
|
# OIDC Configuration
|
|
providerUrl: "https://accounts.google.com"
|
|
clientId: "123456789-abcdef.apps.googleusercontent.com"
|
|
clientSecret: "GOCSPX-your-client-secret"
|
|
callbackUrl: "https://your-domain.com/auth/callback"
|
|
sessionEncryptionKey: "your-32-character-encryption-key-here"
|
|
|
|
# Domain restrictions
|
|
allowedUserDomains: ["your-company.com"]
|
|
|
|
# Security Headers
|
|
securityHeaders:
|
|
enabled: true
|
|
profile: "strict"
|
|
corsEnabled: true
|
|
corsAllowedOrigins:
|
|
- "https://your-frontend.com"
|
|
- "https://*.your-domain.com"
|
|
corsAllowCredentials: true
|
|
customHeaders:
|
|
X-Company: "YourCompany"
|
|
X-Environment: "production"
|
|
|
|
routers:
|
|
secure-app:
|
|
rule: "Host(`your-domain.com`)"
|
|
middlewares:
|
|
- secure-google-oidc
|
|
service: your-app-service
|
|
tls:
|
|
certResolver: letsencrypt
|
|
```
|
|
|
|
### CORS Configuration Details
|
|
|
|
For applications with frontend-backend separation, configure CORS properly:
|
|
|
|
#### Simple CORS (Single Origin)
|
|
```yaml
|
|
securityHeaders:
|
|
corsEnabled: true
|
|
corsAllowedOrigins: ["https://app.example.com"]
|
|
corsAllowCredentials: true
|
|
```
|
|
|
|
#### Wildcard Subdomains
|
|
```yaml
|
|
securityHeaders:
|
|
corsEnabled: true
|
|
corsAllowedOrigins: ["https://*.example.com"]
|
|
corsAllowCredentials: true
|
|
```
|
|
|
|
#### Development with Multiple Ports
|
|
```yaml
|
|
securityHeaders:
|
|
profile: "development"
|
|
corsEnabled: true
|
|
corsAllowedOrigins:
|
|
- "http://localhost:*"
|
|
- "http://127.0.0.1:*"
|
|
```
|
|
|
|
### Security Best Practices
|
|
|
|
1. **Always use HTTPS in production**
|
|
- Set `forceHttps: true`
|
|
- Configure proper TLS certificates
|
|
|
|
2. **Implement proper CSP**
|
|
- Start with strict policy
|
|
- Add exceptions only when necessary
|
|
- Test thoroughly
|
|
|
|
3. **Configure CORS restrictively**
|
|
- Only allow necessary origins
|
|
- Use specific domains instead of wildcards when possible
|
|
|
|
4. **Enable HSTS**
|
|
- Use long max-age values (1 year minimum)
|
|
- Include subdomains when appropriate
|
|
|
|
5. **Monitor security headers**
|
|
- Use browser developer tools to verify headers
|
|
- Test with security scanning tools
|
|
- Regularly review and update policies
|
|
|
|
### Testing Security Headers
|
|
|
|
Use browser developer tools or online tools to verify your security headers:
|
|
|
|
1. **Browser DevTools**: Check Network tab → Response Headers
|
|
2. **Online scanners**: Use securityheaders.com or observatory.mozilla.org
|
|
3. **Command line**: Use `curl -I https://your-domain.com`
|
|
|
|
Example verification:
|
|
```bash
|
|
curl -I https://your-domain.com
|
|
# Should show security headers in response
|
|
``` |