๐Ÿ” OIDC Single Sign-On

May 12, 2026 ยท View on GitHub

Enable Single Sign-On (SSO) with providers like Authentik, Keycloak, Okta, and Azure AD.

๐Ÿš€ Quick Start

  1. Configure Provider: Create an OIDC application in your IdP.
    • Redirect URI: https://<your-pulse-domain>/api/oidc/<provider-id>/callback
    • Scopes: openid, profile, email
  2. Enable in Pulse: Go to Settings โ†’ Security โ†’ Single Sign-On.
  3. Enter Details:
    • Issuer URL: The base URL of your IdP (e.g., https://auth.example.com/application/o/pulse/).
    • Client ID & Secret: From your IdP.
  4. Save: The login page will now show your configured SSO provider button(s).

Tip: To hide the username/password form and only show the SSO button, set PULSE_AUTH_HIDE_LOCAL_LOGIN=true in your environment. You can still access the local login by appending ?show_local=true to the URL (e.g., https://your-pulse-instance/?show_local=true).

โš™๏ธ Configuration

SettingDescription
Issuer URLThe OIDC provider's issuer URL. Must match the iss claim in tokens.
Client IDThe application ID from your provider.
Client SecretThe application secret.
Redirect URLAuto-detected. Override only if running behind a complex proxy setup.
ScopesSpace-separated scopes. Default: openid profile email.
Claim MappingMap email, username, and groups to specific token claims.

Note: Setting OIDC_* environment variables locks those fields in the UI. See CONFIGURATION.md for the full list of overrides.

Access Control

Restrict access to specific users or groups:

  • Allowed Groups: Only users in these groups can login. Requires the groups scope/claim.
  • Allowed Domains: Restrict to specific email domains (e.g., example.com).
  • Allowed Emails: Allow specific email addresses.

Group-to-Role Mapping (Pro and Above)

Automatically assign Pulse roles based on OIDC group membership. When a user logs in, Pulse checks their groups claim and assigns the corresponding roles.

Configuration: Group-role mappings are configured per SSO provider through the UI (or the SSO provider API for automated setup) โ€” there is no environment-variable override. Go to Settings โ†’ Security โ†’ Single Sign-On, edit the provider, and populate Group Role Mappings with entries like:

  • oidc-admins โ†’ admin
  • oidc-operators โ†’ operator
  • oidc-viewers โ†’ viewer

The mappings persist on the provider record as a groupRoleMappings JSON field. Provider-level config (including this field) can be PUT through the SSO provider API for automated setup.

How it works:

  • On each login, Pulse reads the user's groups from the configured groups claim.
  • For each group that matches a mapping, the corresponding role is assigned.
  • Multiple groups can map to multiple roles (user gets all matching roles).
  • Role assignments are updated on every login to reflect current group membership.
  • Role changes are logged to the audit log for compliance tracking.

Example: If a user has groups ["oidc-admins", "developers"] and you have mappings:

  • oidc-admins โ†’ admin
  • developers โ†’ operator

The user will be assigned both admin and operator roles.

Note: Ensure your IdP includes the groups scope and that the groups claim is properly configured. Some providers use groups, others use roles or custom claims.

Long-Lived Sessions with offline_access

For persistent sessions that don't require frequent re-authentication:

  1. Add offline_access scope: Include offline_access in your OIDC scopes (e.g., openid profile email offline_access).
  2. Configure your IdP: Ensure your identity provider issues refresh tokens when offline_access is requested.

How it works:

  • When you login with offline_access, Pulse stores the refresh token alongside your session.
  • When your access token expires, Pulse automatically refreshes it using the stored refresh token.
  • Your session remains valid as long as the refresh token is valid (typically 30-90 days depending on your IdP).
  • If the IdP revokes access (user disabled, token revoked), Pulse detects this on the next refresh attempt and logs you out.

Security considerations:

  • Refresh tokens are stored encrypted at rest.
  • If the IdP configuration changes, existing sessions with mismatched issuers are automatically invalidated.
  • Failed refresh attempts immediately invalidate the session.

๐Ÿ“š Provider Examples

Authentik

  • Type: OAuth2/OpenID (Confidential)
  • Redirect URI: https://pulse.example.com/api/oidc/<provider-id>/callback
  • Signing Key: Must use RS256 (create a certificate/key pair if needed).
  • Issuer URL: https://auth.example.com/application/o/pulse/

Keycloak

  • Client ID: pulse
  • Access Type: Confidential
  • Valid Redirect URIs: https://pulse.example.com/api/oidc/<provider-id>/callback
  • Issuer URL: https://keycloak.example.com/realms/myrealm

Azure AD

  • Redirect URI: https://pulse.example.com/api/oidc/<provider-id>/callback (Web)
  • Issuer URL: https://login.microsoftonline.com/<tenant-id>/v2.0
  • Note: Enable "ID tokens" in Authentication settings.

๐Ÿ”ง Troubleshooting

IssueSolution
invalid_id_tokenIssuer URL mismatch. Check logs (LOG_LEVEL=debug) to see the expected vs. received issuer.
unexpected signature algorithm "HS256"Your IdP is signing with HS256. Configure it to use RS256.
Redirect LoopCheck X-Forwarded-Proto header (must be https) and cookie settings.
Self-Signed CertsSet the CA Bundle field on the SSO provider to a host path readable by Pulse (e.g. /etc/ssl/certs/oidc-ca.pem mounted into the container). The field is stored on the provider record as oidc.caBundle; there is no OIDC_CA_BUNDLE env var.

Debugging

Enable debug logs to trace the OIDC flow:

export LOG_LEVEL=debug
# Restart Pulse

Logs will show discovery, token exchange, and claim parsing details.