Repository navigation
OAuth Authentication Setup
Add SSO to Caddy Proxy Manager with Authentik, Keycloak, Auth0, or any OIDC provider.
- What OAuth Does
- Supported Providers
- Environment Variables
- Provider Setup Guides
- Redirect URI Configuration
- Account Creation
- Account Linking
- Passwords for OAuth Users
- Troubleshooting
Adds a "Sign in with {Provider}" button to the login page. Users can either use their regular password or OAuth. Works with any OIDC-compliant provider.
The flow:
- Click "Sign in with {Provider}"
- Authenticate with your provider
- Get redirected back and logged in
- Optionally link OAuth to existing password accounts
OIDC auto-discovery means you usually just need to set the issuer URL and it figures out the rest.
As of 1.0-RC, OAuth providers can be added and managed from Settings → OAuth Providers in the admin dashboard. This is the recommended approach:
- Navigate to Settings → OAuth Providers and click Add Provider
- Fill in the provider name, client ID, client secret, and issuer URL
- The Callback URL is displayed for each provider — copy it and add it to your OAuth provider's allowed redirect URIs
Environment-configured providers (OAUTH_* env vars) appear as read-only "ENV" entries and can be enabled/disabled from the UI but not edited.
Any OIDC-compliant provider is supported, including:
- Authentik - Open source identity provider
- Keycloak - Open source identity and access management
- Auth0 - Commercial identity platform
- Okta - Enterprise identity provider
- Azure AD - Microsoft identity platform
- Google - Google OAuth (with OIDC)
- Generic OIDC - Any OpenID Connect provider
- OpenID Connect (OIDC) discovery support preferred
- OAuth 2.0 authorization code flow
- Ability to configure redirect URIs
- Client ID and Client Secret
Configure OAuth via environment variables in .env:
# Enable OAuth
OAUTH_ENABLED=true
# Provider information
OAUTH_PROVIDER_NAME="Authentik" # Display name on login page
OAUTH_CLIENT_ID="your-client-id"
OAUTH_CLIENT_SECRET="your-client-secret"
OAUTH_ISSUER="https://auth.example.com/application/o/app/"# Manual endpoint override (if auto-discovery fails)
OAUTH_AUTHORIZATION_URL="https://..."
OAUTH_TOKEN_URL="https://..."
OAUTH_USERINFO_URL="https://..."
# Account creation and linking
AUTH_ALLOW_OAUTH_REGISTRATION=false # Allow first-time OAuth/OIDC users to create accounts
OAUTH_ALLOW_AUTO_LINKING=false # Trust this IdP to prove ownership of the account with the same emailSee Account Creation and Account Linking.
# .env file
BASE_URL="https://proxy.example.com"
# OAuth configuration
OAUTH_ENABLED=true
OAUTH_PROVIDER_NAME="Authentik"
OAUTH_CLIENT_ID="caddy-proxy-manager"
OAUTH_CLIENT_SECRET="your-secret-here"
OAUTH_ISSUER="https://auth.example.com/application/o/caddy-proxy/"
# Optional: auto-linking
OAUTH_ALLOW_AUTO_LINKING=falseSee Environment Variables Reference for detailed documentation.
Step 1: Create Provider in Authentik
-
Navigate to Applications → Providers
-
Click "Create" → Select "OAuth2/OpenID Provider"
-
Configure provider:
- Name:
Caddy Proxy Manager - Authorization flow:
default-provider-authorization-implicit-consent - Client type:
Confidential - Redirect URIs:
{BASE_URL}/api/auth/callback/{provider-id}- Example (environment-configured provider named
Authentik):https://proxy.example.com/api/auth/callback/authentik
- Example (environment-configured provider named
- Name:
-
Click "Create"
-
Note the
Client IDandClient Secret
Step 2: Create Application
-
Navigate to Applications → Applications
-
Click "Create"
-
Configure:
- Name:
Caddy Proxy Manager - Slug:
caddy-proxy - Provider: Select the provider created above
- Name:
-
Save
Step 3: Get Issuer URL
Navigate to your provider and find the "OpenID Configuration URL":
https://auth.example.com/application/o/caddy-proxy/
Step 4: Configure Environment
# .env
OAUTH_ENABLED=true
OAUTH_PROVIDER_NAME="Authentik"
OAUTH_CLIENT_ID="your-client-id"
OAUTH_CLIENT_SECRET="your-client-secret"
OAUTH_ISSUER="https://auth.example.com/application/o/caddy-proxy/"Step 5: Recreate the web container
docker compose up -d webdocker compose restart does not re-read .env, so it would keep the old values.
Step 1: Create Client
-
Navigate to Clients → Create
-
Configure:
- Client ID:
caddy-proxy-manager - Client Protocol:
openid-connect - Root URL:
{BASE_URL}
- Client ID:
-
Click "Save"
Step 2: Configure Client
- Access Type:
confidential - Valid Redirect URIs:
{BASE_URL}/api/auth/callback/{provider-id} - Save
Step 3: Get Client Secret
- Navigate to "Credentials" tab
- Copy the "Secret"
Step 4: Get Issuer URL
Format: https://{keycloak-host}/realms/{realm-name}
Example: https://keycloak.example.com/realms/master
Step 5: Configure Environment
# .env
OAUTH_ENABLED=true
OAUTH_PROVIDER_NAME="Keycloak"
OAUTH_CLIENT_ID="caddy-proxy-manager"
OAUTH_CLIENT_SECRET="your-client-secret"
OAUTH_ISSUER="https://keycloak.example.com/realms/master"Step 1: Create Application
- Navigate to Applications → Applications
- Click "Create Application"
- Choose "Regular Web Application"
- Name:
Caddy Proxy Manager
Step 2: Configure Application
-
Allowed Callback URLs:
{BASE_URL}/api/auth/callback/{provider-id} -
Save
Step 3: Get Credentials
- Note the "Client ID"
- Note the "Client Secret"
- Note the "Domain" (e.g.,
yourtenant.auth0.com)
Step 4: Configure Environment
# .env
OAUTH_ENABLED=true
OAUTH_PROVIDER_NAME="Auth0"
OAUTH_CLIENT_ID="your-client-id"
OAUTH_CLIENT_SECRET="your-client-secret"
OAUTH_ISSUER="https://yourtenant.auth0.com/"For any OIDC-compliant provider:
Step 1: Create OAuth Client
- Create OAuth2/OIDC client in your provider
- Set redirect URI:
{BASE_URL}/api/auth/callback/{provider-id} - Get client ID and secret
Step 2: Find OIDC Discovery URL
Usually: https://your-provider.com/.well-known/openid-configuration
The issuer is the base URL before /.well-known/...
Step 3: Configure Environment
# .env
OAUTH_ENABLED=true
OAUTH_PROVIDER_NAME="My Provider"
OAUTH_CLIENT_ID="your-client-id"
OAUTH_CLIENT_SECRET="your-client-secret"
OAUTH_ISSUER="https://your-provider.com/"The redirect URI is where the OAuth provider sends users after authentication.
{BASE_URL}/api/auth/callback/{provider-id}
The {provider-id} is unique per provider:
-
UI-created providers: a random UUID (e.g.
a1b2c3d4-...) -
Environment-configured providers: derived from
OAUTH_PROVIDER_NAME: lowercased, with each run of characters other thana-zand0-9replaced by-(e.g.Authentik→authentik,My Provider→my-provider)
The exact callback URL for each provider is displayed in Settings → OAuth Providers.
https://proxy.example.com/api/auth/callback/a1b2c3d4-e5f6-7890-abcd-ef1234567890
https://proxy.example.com/api/auth/callback/authentik
Upgrading from < 1.0-RC: the fixed callback URL
/api/auth/callback/oauth2was replaced by a per-provider URL; copy the exact Callback URL from Settings → OAuth Providers into your OAuth provider.
-
Must match BASE_URL exactly
- Include http:// or https://
- Match port if not 80/443
- No trailing slash on base URL
-
Configure in OAuth provider
- Add to allowed redirect URIs
- Some providers allow wildcards, others don't
- Multiple redirect URIs may be supported
-
HTTPS recommended
- Use HTTPS in production
- Some providers require HTTPS
- Local development may use HTTP
A first-time OAuth/OIDC identity creates a CPM account only when AUTH_ALLOW_OAUTH_REGISTRATION=true. It is false by default and applies to UI-configured and environment-configured providers alike. With it off, only identities already linked to an account, or linked through auto-linking, can sign in.
(since v1.13.1) With AUTH_ALLOW_OAUTH_REGISTRATION=false, an OAuth sign-in can no longer create an account even if the client explicitly asks for sign-up.
New OAuth accounts get the user role, unless AUTH_ALLOW_OAUTH_ROLE_FROM_CLAIMS=true lets the IdP's claims set it. See Feature Guide User Management.
(since v1.13.1) New OAuth accounts have no sign-in username for the login page until they set a password (see Passwords for OAuth Users), whatever username the IdP's profile carries. A sign-up is refused when another account signs in with its email address as username (or, for a @localhost address, with the part before @localhost), so that one name never reaches two accounts.
Attaching an OAuth identity to an existing CPM user requires Auto-link accounts to be enabled for that provider: in Settings → OAuth Providers, or with OAUTH_ALLOW_AUTO_LINKING=true for environment-configured providers. The switch marks the provider as trusted to prove that its identity owns the CPM account with the same email address, so leave it off for any IdP where users can register an arbitrary email themselves.
- Signing in through the provider links the identity to the existing user with the matching email.
- Profile → OAuth Connections → Link {Provider} links the provider to the signed-in account. The provider's email must match the signed-in user's email.
Both paths are refused. On the Profile page the Link {Provider} button is disabled, with the hint "Enable "Auto-link accounts" for {Provider} in Settings → OAuth Providers to allow linking."
Users can unlink their OAuth account from Profile → OAuth Connections → Unlink OAuth Account. This removes all OAuth account links and leaves username/password sign-in.
(since v1.13.1) Unlinking requires a working username/password sign-in: a password and a sign-in username the login page accepts (see Feature Guide User Management). Otherwise the button is not shown, the Profile page explains what is missing, and the request fails with "Cannot unlink OAuth: You must set a password first". Users who set a password on an older release must change it once before the button appears. The confirmation dialog shows the username to sign in with afterwards.
Better Auth's own /api/auth/unlink-account endpoint is disabled; unlinking works only through the Profile page.
(since v1.13.1)
An OAuth-only user can add a password from Profile → Set Password to sign in with credentials as well:
- It only works within 10 minutes of signing in, because there is no current password to prove. Otherwise it fails with "Please sign in again before setting a password.": sign out, sign in again with OAuth, and set the password right away.
- The password must meet the password policy (12+ characters, upper- and lowercase letters, a digit and a special character).
- Setting it signs out the user's other dashboard sessions and all of their forward-auth sessions. API tokens are kept.
-
(since v1.13.1) An account without a usable sign-in username gets its own email address as username when that address is 3–255 characters from
A-Z a-z 0-9 _ . @ -and no other account signs in with it or has it as its email address. The user then signs in at/loginwith the Sign-in username shown on the Profile page. CPM no longer makes a username up from the email address: for any other address (e.g. one containing+), the Profile page says that an administrator has to set a sign-in username, which they do on the Users page (edit → Username).
See Feature Guide User Management for the password policy and sign-in usernames.
Error: redirect_uri_mismatch
Cause: BASE_URL doesn't match configured redirect URI
Solution:
-
Check
BASE_URLin.env:BASE_URL="https://proxy.example.com" # No trailing slash
-
Verify redirect URI in OAuth provider (check Settings → OAuth Providers for exact URL):
{BASE_URL}/api/auth/callback/{provider-id} -
Recreate the web container (
docker compose restartdoes not re-read.env):docker compose up -d web
Error: Failed to discover OIDC endpoints
Cause: OIDC discovery endpoint not accessible or malformed
Solution:
-
Test discovery endpoint manually:
curl https://auth.example.com/application/o/app/.well-known/openid-configuration
-
If discovery fails, use manual endpoints:
OAUTH_AUTHORIZATION_URL="https://..." OAUTH_TOKEN_URL="https://..." OAUTH_USERINFO_URL="https://..."
-
Check issuer URL format (trailing slash may be required)
Cause: OAuth not properly enabled
Solution:
-
Verify
OAUTH_ENABLED=truein.env -
Check all required variables are set:
- OAUTH_CLIENT_ID
- OAUTH_CLIENT_SECRET
- OAUTH_ISSUER
-
Check logs:
docker compose logs web | grep -i oauth -
Recreate the web container after changes (
docker compose up -d web)
Error: invalid_client or unauthorized_client
Cause: Client ID/secret mismatch or client not authorized
Solution:
- Verify client ID and secret are correct
- Check client is enabled in OAuth provider
- Verify redirect URI is configured
- Check client type is "confidential" not "public"
Error: Failed to get user information
Cause: Userinfo endpoint not accessible or token invalid
Solution:
- Check OAUTH_USERINFO_URL if manually set
- Verify token scopes include
profileandemail - Check OAuth provider logs
- Test userinfo endpoint manually with access token
Symptom: Can't link OAuth to existing account
Cause: Auto-linking is disabled for the provider, or the provider's email does not match the CPM account's email
Solution:
-
Enable Auto-link accounts for the provider in Settings → OAuth Providers, or for an environment-configured provider:
OAUTH_ALLOW_AUTO_LINKING=true
Only do this for an IdP you trust to prove email ownership (see Account Linking).
-
Make sure the email address at the provider matches the CPM account's email
Symptom: A user without a CPM account signs in with OAuth and is not logged in
Cause: AUTH_ALLOW_OAUTH_REGISTRATION is false (the default), so first-time identities cannot create accounts
Solution:
- Create the account on the Users page with the same email and enable auto-linking for the provider, or
- Set
AUTH_ALLOW_OAUTH_REGISTRATION=trueand recreate the web container (docker compose up -d web)
See Account Creation.
Error: Please sign in again before setting a password.
Cause: The account has no password yet, and the current session was signed in more than 10 minutes ago
Solution: Sign out, sign in again with OAuth, and set the password right away. See Passwords for OAuth Users.
Symptom: The Profile page shows no Unlink OAuth Account button
Cause: The login page could not sign the user in with a password: no password is set, the password was set on an older release, or the account has no usable sign-in username
Solution:
- Follow the hint on the Profile page
- Set a password, or change it once if it was set on an older release
- If the Profile page says the account has no sign-in username the login page can use, ask an administrator to set one on the Users page (edit → Username); see Troubleshooting#no-sign-in-username-on-the-profile-page
OAuth login is also available on the forward auth login portal. When a user accesses a forward-auth-protected proxy host and is redirected to the CPM login portal, they can sign in with OAuth instead of credentials.
This means OAuth users can access forward-auth-protected apps without needing a separate password — as long as they are in the host's access list (directly or via a group).
See Feature Guide Forward Auth for details.
- Environment Variables Reference - OAuth variable documentation
- Security Configuration - OAuth security considerations
- Feature Guide Forward Auth - Forward auth portal (uses OAuth login)
- Feature Guide User Management - User roles, passwords, sign-in usernames and OAuth user management
- Installation Guide - Initial setup
- Troubleshooting - General troubleshooting
Need help? Open an issue with your OAuth provider details.