Skip to main content

OpenID Connect

Semaphore supports authentication via OpenID Connect (OIDC).

Links:

Example of SSO provider configuration:

{
"oidc_providers": {
"mysso": {
"display_name": "Sign in with MySSO",
"color": "orange",
"icon": "login",
"provider_url": "https://mysso-provider.com",
"client_id": "***",
"client_secret": "***",
"redirect_url": "https://your-domain.com/api/auth/oidc/mysso/redirect"
}
}
}

Configure via environment variable​

When running in containers it may be convenient to configure providers using a single environment variable:

SEMAPHORE_OIDC_PROVIDERS='{
"github": {
"client_id": "***",
"client_secret": "***"
}
}'

This value must be a valid JSON string matching the oidc_providers structure above.

All SSO provider options:

ParameterDescription
display_nameProvider name which displayed on Login screen.
iconMDI-icon which displayed before of provider name on Login screen.
colorProvider name which displayed on Login screen.
client_idProvider client ID.
client_id_fileThe path to the file where the provider's client ID is stored. Has less priorty then client_id.
client_secretProvider client Secret.
client_secret_fileThe path to the file where the provider's client secret is stored. Has less priorty then client_secret.
redirect_url
provider_url
scopes
username_claimUsername claim expression*.
email_claimEmail claim expression*.
name_claimProfile Name claim expression*.
groups_claimName of the token claim with the user's groups. Used by group mappings.
orderPosition of the provider button on the Sign in screen.
allow_idp_initiatedEnable IdP-initiated login for this provider. Default false.
return_via_statePass the post-login return path via the OAuth state parameter instead of the redirect URL. Default true.
endpoint.issuer
endpoint.auth
endpoint.token
endpoint.userinfo
endpoint.jwks
endpoint.algorithms

*Claim expression​

Example of claim expression:

email | {{ .username }}@your-domain.com

Semaphore is attempting to claim the email field first. If it is empty, the expression following it is executed.

The expression "username_claim": "|" generates a random username for each user who logs in through the provider.

Project roles from groups Enterprise​

info

Group mappings are available in the Semaphore Enterprise edition.

A group mapping gives a project role to every user in a group of the identity provider. For example, a mapping can give the members of the devops group the Manager role in the Infrastructure project.

Send groups in the token​

Set groups_claim to the name of the claim with the user's groups. Without it, sign-ins through the provider do not change roles from group mappings.

The groups must be in the ID token. Many identity providers, for example Dex, send them only when Semaphore asks for the groups scope:

{
"oidc_providers": {
"dex": {
"display_name": "Sign in with Dex",
"provider_url": "https://dex.your-domain.com",
"client_id": "***",
"client_secret": "***",
"redirect_url": "https://your-domain.com/api/auth/oidc/dex/redirect",
"scopes": ["openid", "email", "profile", "groups"],
"groups_claim": "groups"
}
}
}

Create a group mapping​

  1. Open the bottom-left admin menu and select Group mappings.
  2. Select New mapping.
  3. Choose the provider, enter the group name, and choose the project and the role.
  4. Select Save.

The group name must match the name in the token exactly, including upper and lower case. To change a group mapping, delete it and create a new one.

Semaphore updates roles from group mappings each time a user signs in through the provider. A user who leaves a group keeps the role until the next sign-in. To make such changes take effect sooner, limit the session life with auth.max_session_life_hours.

IdP-initiated login​

By default Semaphore only supports SP-initiated sign-in: the user opens Semaphore, clicks the provider button, and is redirected to the identity provider (IdP).

With IdP-initiated login the journey can start at the identity provider instead — for example by clicking the Semaphore tile in the Okta dashboard, Azure My Apps, or a Keycloak / Authentik application launcher.

Semaphore implements this using the standard Third-Party Initiated Login mechanism (OpenID Connect Core 1.0 §4). The IdP redirects the browser to a dedicated Initiate Login URI, and Semaphore then starts a normal Authorization Code flow. The actual authentication is still a full, secure code exchange — Semaphore never accepts an unsolicited token.

Enabling it​

Set allow_idp_initiated to true for the provider:

{
"oidc_providers": {
"mysso": {
"display_name": "Sign in with MySSO",
"provider_url": "https://mysso-provider.com",
"client_id": "***",
"client_secret": "***",
"redirect_url": "https://your-domain.com/api/auth/oidc/mysso/redirect",
"allow_idp_initiated": true
}
}
}

Configuring the identity provider​

In your IdP, set the application's Initiate Login URI to:

https://your-domain.com/api/auth/oidc/<provider-id>/initiate

where <provider-id> is the key under oidc_providers (for example mysso).

The IdP must send the iss (issuer) parameter to this endpoint; Semaphore rejects requests whose iss does not match the configured provider. The optional login_hint parameter is forwarded to the IdP, and an optional target_link_uri sets the page to open after login (it must point back to Semaphore, otherwise it is ignored).

Provider-specific notes:

  • Okta — set Login initiated by to Either Okta or App (or App Only) and fill in the Initiate login URI. Okta sends both iss and target_link_uri.
  • Keycloak / Authentik / Ping / OneLogin — set the application's launch / home URL to the Initiate Login URI.
  • Azure AD / Entra — My Apps uses an SP-initiated start URL and does not always send iss; point the start URL at https://your-domain.com/api/auth/oidc/<provider-id>/login instead.

Security​

  • IdP-initiated login is off by default and must be enabled per provider.
  • The iss parameter is validated against the configured issuer to prevent provider mix-up.
  • target_link_uri is accepted only when it points back to Semaphore (no open redirects).
  • The flow goes through the full Authorization Code exchange with CSRF state and a nonce, so a captured or replayed token cannot be used to sign in.

Sign in screen​

For each of the configured providers, an additional login button is added to the login page:

Screenshot of the Semaphore login page, with two login buttons. One says "Sign In", the other says "Sign in with MySSO"