OIDC Authentication¶
Enlace supports OpenID Connect (OIDC) for Single Sign-On (SSO). This allows users to authenticate using an external identity provider instead of (or in addition to) a local email/password.
Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
OIDC_ENABLED |
Yes | false |
Set to true to enable OIDC authentication |
OIDC_ISSUER_URL |
Yes | — | The OIDC provider's issuer URL (must support discovery at /.well-known/openid-configuration) |
OIDC_CLIENT_ID |
Yes | — | The client ID from your OIDC provider |
OIDC_CLIENT_SECRET |
Yes | — | The client secret from your OIDC provider |
OIDC_REDIRECT_URL |
Yes | — | The callback URL: https://<your-enlace-domain>/api/v1/auth/oidc/callback |
OIDC_SCOPES |
No | openid email profile |
Space-separated list of OIDC scopes to request |
How It Works¶
- When OIDC is enabled, a "Sign in with SSO" button appears on the login page.
- Clicking it redirects the user to the OIDC provider for authentication.
- After successful authentication, the provider redirects back to Enlace's callback URL.
- Enlace extracts the user's email, name, and subject from the ID token.
- If a local user with the same email exists and the provider reports
email_verified: true, the OIDC identity is automatically linked to that account. If the provider has not verified the email, sign-in is rejected with"sign-in rejected: your identity provider has not verified your email address"— this prevents an unverified email from silently hijacking an existing account. - If no matching user exists, a new account is created.
Existing users can also link/unlink their OIDC identity from the Settings → Security tab.
Important: You cannot unlink an OIDC identity from an account that has no local password — doing so would lock you out entirely. Before unlinking, make sure your account has a local password set. You can set one via Settings → Security or the
PUT /api/v1/me/passwordendpoint. Thehas_passwordfield inGET /api/v1/meshows whether your account has a password.Note: The OIDC provider must return an
OIDC and Two-Factor Authentication (2FA)¶
OIDC authentication and TOTP-based 2FA are mutually exclusive in Enlace:
- Linking OIDC disables 2FA. When you link an OIDC identity to an account that already has 2FA enabled, 2FA is automatically disabled. The OIDC provider is responsible for its own multi-factor authentication.
- OIDC accounts cannot enable 2FA. The
POST /api/v1/me/2fa/setup,POST /api/v1/me/2fa/confirm,POST /api/v1/me/2fa/disable, andPOST /api/v1/me/2fa/recovery-codesendpoints return HTTP 403 for accounts with a linked OIDC identity. - OIDC logins skip the 2FA verification step. Even if
REQUIRE_2FAis set totrue, users authenticated via OIDC proceed directly to the application without a TOTP challenge.
If you want 2FA enforcement for SSO users, configure MFA at the OIDC provider level.
General Setup¶
- Register Enlace as a client/application in your OIDC provider.
- Set the callback/redirect URL to:
https://<your-enlace-domain>/api/v1/auth/oidc/callback - Copy the Client ID and Client Secret from the provider.
- Note the provider's Issuer URL (the base URL that serves
/.well-known/openid-configuration). - Configure the environment variables and restart Enlace.
Example .env¶
OIDC_ENABLED=true
OIDC_ISSUER_URL=https://auth.example.com
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URL=https://enlace.example.com/api/v1/auth/oidc/callback
OIDC_SCOPES=openid email profile
Pocket ID Example¶
Pocket ID is a lightweight, self-hosted OIDC provider that uses passkey authentication. It's a great choice if you want simple SSO for your self-hosted services without the complexity of Keycloak or similar solutions.
1. Set Up Pocket ID¶
Follow the Pocket ID installation guide to deploy your instance. A minimal Docker Compose setup looks like:
services:
pocket-id:
image: ghcr.io/pocket-id/pocket-id:latest
ports:
- "3000:80"
environment:
- PUBLIC_APP_URL=https://auth.example.com
volumes:
- pocket-id-data:/app/backend/data
restart: unless-stopped
volumes:
pocket-id-data:
2. Create an OIDC Client in Pocket ID¶
- Log in to your Pocket ID admin panel.
- Navigate to OIDC Clients and create a new client.
- Set the Name to
Enlace(or any name you prefer). - Set the Callback URL to:
https://<your-enlace-domain>/api/v1/auth/oidc/callback - Copy the Client ID and Client Secret.
- Note the OIDC Discovery URL — the issuer URL will be your Pocket ID domain (e.g.,
https://auth.example.com).
3. Configure Enlace¶
Set the following environment variables for Enlace (shown here in a docker-compose.yml):
services:
enlace:
image: enlace:latest
ports:
- "8080:8080"
environment:
- PORT=8080
- DATABASE_PATH=/app/data/enlace.db
- BASE_URL=https://enlace.example.com
- OIDC_ENABLED=true
- OIDC_ISSUER_URL=https://auth.example.com
- OIDC_CLIENT_ID=<client-id-from-pocket-id>
- OIDC_CLIENT_SECRET=<client-secret-from-pocket-id>
- OIDC_REDIRECT_URL=https://enlace.example.com/api/v1/auth/oidc/callback
- OIDC_SCOPES=openid email profile
volumes:
- enlace-data:/app/data
- enlace-uploads:/app/uploads
restart: unless-stopped
volumes:
enlace-data:
enlace-uploads:
4. Test the Integration¶
- Open Enlace in your browser.
- On the login page, click "Sign in with SSO".
- You should be redirected to Pocket ID to authenticate with your passkey.
- After authentication, you'll be redirected back to Enlace and logged in.
SSO and Two-Factor Authentication¶
SSO (OIDC) and 2FA are mutually exclusive. When you link an OIDC identity to a Enlace account — either by signing in for the first time or by linking via the Settings → Security tab — any existing TOTP 2FA configuration on that account is automatically and permanently removed.
Why?¶
When authentication is delegated to an identity provider, the provider is responsible for all factors of authentication (including second factors such as passkeys, hardware tokens, or its own TOTP). Adding an additional Enlace-managed TOTP layer on top would create redundancy without meaningful security benefit, and could create confusing or inaccessible account states.
Behaviour for SSO-linked accounts¶
| Situation | What happens |
|---|---|
| User links an OIDC identity (first login or explicit link) | Any active 2FA is silently removed |
SSO user attempts to set up 2FA (POST /me/2fa/setup) |
HTTP 403 — "2FA is not available for SSO accounts" |
| SSO user attempts to confirm setup, disable, or regenerate recovery codes | HTTP 403 — same error |
| SSO user logs in | 2FA challenge step is skipped entirely |
| 2FA UI in Settings → Security | Hidden for SSO-linked accounts |
Migrating from 2FA to SSO¶
If you have 2FA enabled and want to switch to SSO, simply link your OIDC identity via Settings → Security → Linked Accounts. Your 2FA configuration will be removed automatically. You do not need to disable 2FA manually first.
Migrating from SSO to local password + 2FA¶
- Ensure your account has a local password set (Settings → Security → Change Password or
PUT /api/v1/me/password). - Unlink your OIDC identity via Settings → Security → Linked Accounts.
- You can now enroll in 2FA under Settings → Security → Two-Factor Authentication.
Troubleshooting¶
"OIDC is not enabled"¶
- Verify
OIDC_ENABLEDis set totrue. - Ensure all required OIDC environment variables are set.
- Check the application logs for startup errors related to OIDC provider discovery.
"failed to exchange code" or redirect errors¶
- Confirm
OIDC_REDIRECT_URLexactly matches the callback URL registered in your OIDC provider. - Make sure Enlace can reach the OIDC provider's issuer URL from the server (DNS, network, TLS).
- If running behind a reverse proxy, ensure
BASE_URLreflects the public-facing URL.
"OIDC provider did not return email"¶
- The provider must include an
emailclaim in the ID token. - Ensure the
openidandemailscopes are included inOIDC_SCOPES. - In Pocket ID, make sure the user has an email address configured.
"sign-in rejected: your identity provider has not verified your email address"¶
- Enlace only auto-links an OIDC identity to an existing local account when the provider includes
"email_verified": truein the ID token. This prevents an unverified email address from silently taking over a pre-existing account. - Check whether your provider sets
email_verified. Some providers (e.g. certain Keycloak realm settings) do not mark emails as verified by default. - Options to resolve this:
- Enable email verification in the provider (preferred).
- Configure the provider to include
"email_verified": truein the ID token (some providers let you set this unconditionally for trusted internal users). - If no matching local account exists, Enlace will create a new account regardless of
email_verified— the restriction applies only to auto-linking to an account that already exists.
State mismatch errors¶
- This usually indicates a cookie issue. Ensure cookies are not being stripped by a reverse proxy.
- If using HTTPS, make sure TLS is properly terminated and
SameSitecookie rules are not blocking the flow.