# OIDC Provider Configuration Guide

Configuration reference for each supported OIDC provider.

## Table of Contents

- [Provider Support Matrix](#provider-support-matrix)
- [Google](#google)
- [Microsoft Azure AD](#microsoft-azure-ad)
- [Auth0](#auth0)
- [Okta](#okta)
- [Keycloak](#keycloak)
- [AWS Cognito](#aws-cognito)
- [GitLab](#gitlab)
- [GitHub](#github)
- [Generic OIDC](#generic-oidc)
- [Automatic Scope Filtering](#automatic-scope-filtering)

---

## Provider Support Matrix

| Provider | OIDC Support | Refresh Tokens | Auto-Detection | ID Tokens |
|----------|-------------|----------------|----------------|-----------|
| Google | Full | Yes | `accounts.google.com` | Yes |
| Azure AD | Full | Yes | `login.microsoftonline.com`, `sts.windows.net` | Yes |
| Auth0 | Full | Yes | `*.auth0.com` | Yes |
| Okta | Full | Yes | `*.okta.com`, `*.oktapreview.com`, `*.okta-emea.com` | Yes |
| Keycloak | Full | Yes | host containing `keycloak`, or `/realms/` in path (matches both `/auth/realms/` legacy and `/realms/` modern) | Yes |
| AWS Cognito | Full | Yes | `cognito-idp.*.amazonaws.com` | Yes |
| GitLab | Full | Yes | `gitlab.com` | Yes |
| GitHub | OAuth 2.0 Only | No | `github.com` | No |
| Generic | Full | Yes | Any OIDC endpoint | Yes |

---

## Google

### Provider URL

```yaml
providerURL: "https://accounts.google.com"
```

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-google
spec:
  plugin:
    traefikoidc:
      providerURL: "https://accounts.google.com"
      clientID: "your-id.apps.googleusercontent.com"
      clientSecret: "your-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - email
        - profile
      allowedUserDomains:
        - "your-gsuite-domain.com"  # Optional: Workspace restriction
      forceHttps: true
      enablePkce: true
```

### Google-Specific Features

- **Automatic offline access**: Middleware adds `access_type=offline` and `prompt=consent`
- **Scope filtering**: Automatically removes unsupported `offline_access` scope
- **Workspace domains**: Restrict to specific Google Workspace domains via `hd` claim

### Google Cloud Console Setup

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create or select a project
3. Navigate to APIs & Services > Credentials
4. Create OAuth 2.0 Client ID (Web application)
5. Add authorized redirect URI: `https://your-domain.com/oauth2/callback`
6. Configure OAuth consent screen (must be "Published" for production)

---

## Microsoft Azure AD

### Provider URL

```yaml
# Single tenant
providerURL: "https://login.microsoftonline.com/{tenant-id}/v2.0"

# Multi-tenant
providerURL: "https://login.microsoftonline.com/common/v2.0"
```

### Basic Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-azure
spec:
  plugin:
    traefikoidc:
      providerURL: "https://login.microsoftonline.com/common/v2.0"
      clientID: "your-azure-client-id"
      clientSecret: "your-azure-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
        - offline_access
      allowedRolesAndGroups:
        - "App.Users"
        - "Admin.Group"
      forceHttps: true
```

### With Application ID URI (API Access)

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-azure-api
spec:
  plugin:
    traefikoidc:
      providerURL: "https://login.microsoftonline.com/common/v2.0"
      clientID: "your-azure-client-id"
      clientSecret: "your-azure-client-secret"
      audience: "api://your-azure-client-id"  # Application ID URI
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      forceHttps: true
```

### Users Without Email

```yaml
userIdentifierClaim: sub  # Options: sub, oid, upn, preferred_username
allowedUsers:
  - "user-object-id-1"
  - "user-object-id-2"
```

### Azure AD 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/oauth2/callback`
5. Create client secret in Certificates & secrets
6. Configure Token Configuration for group claims

---

## Auth0

### Provider URL

```yaml
providerURL: "https://your-domain.auth0.com"
```

### Basic Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-auth0
spec:
  plugin:
    traefikoidc:
      providerURL: "https://your-domain.auth0.com"
      clientID: "your-auth0-client-id"
      clientSecret: "your-auth0-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
        - offline_access
      postLogoutRedirectUri: "https://your-app.com"
      forceHttps: true
      enablePkce: true
```

### With Custom API Audience

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-auth0-api
spec:
  plugin:
    traefikoidc:
      providerURL: "https://your-domain.auth0.com"
      clientID: "your-auth0-client-id"
      clientSecret: "your-auth0-client-secret"
      audience: "https://api.your-domain.com"  # API identifier
      strictAudienceValidation: true
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      roleClaimName: "https://your-app.com/roles"  # Namespaced claim
      groupClaimName: "https://your-app.com/groups"
      allowedRolesAndGroups:
        - admin
        - editor
```

### Auth0 Action for Custom Claims

```javascript
exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://your-app.com/';
  if (event.authorization) {
    api.idToken.setCustomClaim(namespace + 'roles', event.authorization.roles);
    api.idToken.setCustomClaim('email', event.user.email);
  }
};
```

### Auth0 Setup

1. Go to [Auth0 Dashboard](https://manage.auth0.com/)
2. Create Regular Web Application
3. Configure Allowed Callback URLs: `https://your-domain.com/oauth2/callback`
4. Configure Allowed Logout URLs: `https://your-domain.com/oauth2/logout`
5. Enable OIDC Conformant in Advanced Settings
6. Create API in APIs section for custom audiences

See [AUTH0_AUDIENCE_GUIDE.md](AUTH0_AUDIENCE_GUIDE.md) for detailed audience configuration.

---

## Okta

### Provider URL

```yaml
providerURL: "https://your-domain.okta.com"
# Or with custom authorization server:
providerURL: "https://your-domain.okta.com/oauth2/default"
```

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-okta
spec:
  plugin:
    traefikoidc:
      providerURL: "https://your-domain.okta.com"
      clientID: "your-okta-client-id"
      clientSecret: "your-okta-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
        - groups
        - offline_access
      allowedRolesAndGroups:
        - admin
        - "Everyone"
      forceHttps: true
      enablePkce: true
```

### Okta Setup

1. Access Okta Admin Console
2. Go to Applications > Create App Integration
3. Select OIDC - OpenID Connect > Web Application
4. Configure Sign-in redirect URIs: `https://your-domain.com/oauth2/callback`
5. Configure Sign-out redirect URIs: `https://your-domain.com/oauth2/logout`
6. Enable Authorization Code and Refresh Token grant types
7. Configure Groups claim in authorization server

---

## Keycloak

### Provider URL

```yaml
providerURL: "https://keycloak.your-domain.com/realms/{realm-name}"
```

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-keycloak
spec:
  plugin:
    traefikoidc:
      providerURL: "https://keycloak.company.com/realms/your-realm"
      clientID: "your-keycloak-client-id"
      clientSecret: "your-keycloak-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
        - roles
        - groups
        - offline_access
      allowedRolesAndGroups:
        - admin
        - editor
      forceHttps: true
      enablePkce: true
```

### Internal Network Deployment

For private IP addresses (Docker networks, Kubernetes):

```yaml
providerURL: "https://192.168.1.100:8443/realms/your-realm"
allowPrivateIPAddresses: true  # Required for private IPs
```

### Keycloak Client Setup

1. Access Keycloak Admin Console
2. Select your realm
3. Go to Clients > Create client
4. Set Client Protocol: openid-connect
5. Set Access Type: confidential
6. Add Valid Redirect URIs: `https://your-domain.com/oauth2/callback`
7. Generate client secret in Credentials tab
8. Configure mappers to add claims to ID Token:
   - Email: User Property mapper with "Add to ID token" enabled
   - Roles: User Client Role mapper with "Add to ID token" enabled
   - Groups: Group Membership mapper with "Add to ID token" enabled

See [KEYCLOAK_SETUP_GUIDE.md](KEYCLOAK_SETUP_GUIDE.md) for detailed step-by-step setup instructions, mapper configuration, troubleshooting, and performance optimization.

---

## AWS Cognito

### Provider URL

```yaml
providerURL: "https://cognito-idp.{region}.amazonaws.com/{user-pool-id}"
```

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-cognito
spec:
  plugin:
    traefikoidc:
      providerURL: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_ABCDEF123"
      clientID: "your-cognito-client-id"
      clientSecret: "your-cognito-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
        - aws.cognito.signin.user.admin
      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/oauth2/callback`
   - Sign out URLs: `https://your-domain.com/oauth2/logout`
   - OAuth flows: Authorization code grant
4. Configure hosted UI domain (optional)
5. Set up groups for role-based access

---

## GitLab

### Provider URL

```yaml
# GitLab.com
providerURL: "https://gitlab.com"

# Self-hosted
providerURL: "https://gitlab.your-company.com"
```

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-gitlab
spec:
  plugin:
    traefikoidc:
      providerURL: "https://gitlab.com"
      clientID: "your-gitlab-application-id"
      clientSecret: "your-gitlab-application-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
        # Note: GitLab doesn't require offline_access scope
        # Refresh tokens are issued automatically with openid
      allowedRolesAndGroups:
        - developers
        - maintainers
      forceHttps: true
      enablePkce: true
```

### GitLab 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/oauth2/callback`
5. Save and note Application ID and Secret

---

## GitHub

### Provider URL

```yaml
providerURL: "https://github.com"
```

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oauth-github
spec:
  plugin:
    traefikoidc:
      providerURL: "https://github.com/login/oauth"
      clientID: "your-github-client-id"
      clientSecret: "your-github-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - user:email
        - read:user
      allowedUsers:
        - "github-username"
      forceHttps: true
```

### Limitations

- **OAuth 2.0 only** - Not OpenID Connect
- **No ID tokens** - Only access tokens for API calls
- **No refresh tokens** - Users must re-authenticate on expiry
- **No standard claims** - User info requires API calls

Use GitHub only for API access, not for user authentication with claims.

### GitHub Setup

1. Go to GitHub Settings > Developer settings > OAuth Apps
2. Create new OAuth App
3. Set Authorization callback URL: `https://your-domain.com/oauth2/callback`
4. Note Client ID and generate Client Secret

---

## Generic OIDC

For any OIDC-compliant provider not listed above.

### Configuration

```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: oidc-generic
spec:
  plugin:
    traefikoidc:
      providerURL: "https://oidc.your-provider.com"
      clientID: "your-client-id"
      clientSecret: "your-client-secret"
      callbackURL: "/oauth2/callback"
      sessionEncryptionKey: "your-32-char-encryption-key-here"
      scopes:
        - openid
        - profile
        - email
      forceHttps: true
      enablePkce: true
```

### Requirements

- Provider must expose `.well-known/openid-configuration` endpoint
- Must support authorization code flow
- ID tokens must contain required claims (email, sub, etc.)

---

## Automatic Scope Filtering

The middleware automatically filters OAuth scopes based on the provider's declared capabilities.

### How It Works

1. Fetches provider's `.well-known/openid-configuration`
2. Extracts `scopes_supported` field
3. Filters requested scopes to only include supported ones
4. Falls back to all requested scopes if provider doesn't declare supported scopes

### Example: Self-Hosted GitLab

Self-hosted GitLab may reject `offline_access` scope:

```yaml
scopes:
  - openid
  - profile
  - email
  - offline_access  # Will be automatically filtered out if unsupported
```

The middleware will:
1. Read GitLab's discovery document
2. Detect `offline_access` is NOT in `scopes_supported`
3. Filter it out automatically
4. Authentication succeeds

### Logging

```
INFO: ScopeFilter: Filtered unsupported scopes: [offline_access]
DEBUG: ScopeFilter: Final filtered scopes: [openid profile email]
```

### Troubleshooting

If a provider rejects scopes even after filtering:
1. Check the provider's discovery document: `curl https://provider/.well-known/openid-configuration`
2. Use `overrideScopes: true` with only supported scopes
3. Review middleware debug logs for filtering decisions
