> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qumo-deploy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Single Sign-On (OIDC)

> Connect your organization to its identity provider over OpenID Connect, test the connection, and activate it.

# Single Sign-On (OIDC)

An organization can connect Qumo Deploy to its own identity provider (IdP) over
OpenID Connect. Once the connection is active, people at your organization's verified
domains sign in with your IdP.

Setup has the same four steps for every IdP. In the console they are all on
**Organization settings → Single sign-on** (`/organization/sso`); each step also shows the
API call that does the same. The IdP guides cover the IdP side:

* [Okta](https://docs.qumo-deploy.com/guides/sso/okta.md)
* [Microsoft Entra ID](https://docs.qumo-deploy.com/guides/sso/microsoft-entra-id.md)
* [Google Workspace](https://docs.qumo-deploy.com/guides/sso/google-workspace.md)

## Before you start

* **A plan with SSO.** Single sign-on is on Pay as you go and Enterprise. On Free, saving a
  connection or starting a test login is refused with `402`; see
  [Pricing & Plans](https://docs.qumo-deploy.com/console/billing.md). Disabling or deleting a connection is never refused.
* **Organization Admin.** The connection's permissions (`organization.sso_connection_*`) are
  held by Organization Admin, and a token needs the `organization:admin` scope; see
  [Scopes & Permissions](https://docs.qumo-deploy.com/guides/scopes-permissions.md).
* **A verified domain.** A connection is used only for sign-ins at the organization's verified
  domains (exact domain only). Add a domain with `POST /v1/organizations/{id}/domains`, publish
  the TXT record it returns at `_qumo-verification.<domain>`, then verify it with
  `POST /v1/organizations/{id}/domains/{domainID}/verify`.

An organization has one OIDC connection.

## 1. Create the app at your IdP

Create an OIDC **web application** that uses the **authorization code** flow, and register
this redirect URI:

```
https://<console host>/oauth/sso/callback
```

For example `https://console.qumo-deploy.com/oauth/sso/callback`. The console's Single
sign-on page shows the exact value with a copy button, as does the connection's
`redirect_uri` field (`GET /v1/organizations/{id}/sso/oidc`). Note the app's **issuer**,
**client ID** and **client secret**.

## 2. Save the connection

In the console, enter the issuer URL, client ID and client secret on the Single sign-on page
and select **Save**; the scopes and the claim mapping (under **Claim mapping**) have the
defaults below. With the API:

```bash theme={null}
curl -X PUT "https://api.qumo-deploy.com/v1/organizations/$ORG_ID/sso/oidc" \
  -H "Authorization: Bearer $QUMO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer": "https://acme.okta.com",
    "client_id": "0oa8f2k3l9XyZ1abc357",
    "client_secret": "<client secret>"
  }'
```

| Field | |
| :- | :- |
| `issuer` | The IdP's issuer, an https URL exactly as its discovery document states it. |
| `client_id` | The app's client ID. |
| `client_secret` | Required when creating. Omit it on later saves to keep the stored one. |
| `scopes` | Optional; must include `openid`. Defaults to `openid email profile`. |
| `claims` | Optional claim names for `email`, `email_verified`, `name` and `groups`. Each defaults to the claim of the same name. |
| `status` | `draft`, `active` or `disabled`. A new connection is `draft`. |

Every save fetches `<issuer>/.well-known/openid-configuration` and is refused with `422`
unless the document states the same issuer, its endpoints are https, it supports the `code`
response type and RS256 or ES256 ID tokens, and it does not rule out S256 PKCE. The issuer
must be reachable on the public internet.

The client secret is encrypted at rest and never shown again: the API returns only whether
one is set and its last four characters.

## 3. Run a test login

Start a test login from the console while signed in as an Organization Admin: **Run test
login** on the Single sign-on page (`POST /v1/organizations/{id}/sso/oidc/test`). It must be started on the console's host,
where the sign-in returns; elsewhere it is refused with `409`. You are sent to your IdP,
sign in there, and come back to the console with the result, which is also recorded on the
connection (`last_test_result`):

| Result | Meaning |
| :- | :- |
| `passed` | The ID token was valid and carried a verified email. |
| `idp_denied` | The IdP sent you back with an error instead of a code, for example because you are not allowed to use the app. |
| `exchange_failed` | The authorization code could not be exchanged: usually a wrong client secret or redirect URI. |
| `invalid_token` | The ID token failed validation (signature, issuer, audience, expiry or nonce). |
| `email_missing` | The ID token has no value in the `email` claim mapping. |
| `email_unverified` | The `email_verified` claim mapping is missing or not `true`. |
| `discovery_failed` | The issuer's discovery document or keys could not be fetched. |
| `configuration_changed` | The connection was changed or deleted while the test ran. Run it again. |

The test login only checks the connection; it never signs you in or creates an account.

## 4. Activate

In the console, select **Activate** on the Single sign-on page; it is available once the
current configuration has passed a test login. With the API:

```bash theme={null}
curl -X PUT "https://api.qumo-deploy.com/v1/organizations/$ORG_ID/sso/oidc" \
  -H "Authorization: Bearer $QUMO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "issuer": "https://acme.okta.com", "client_id": "0oa8f2k3l9XyZ1abc357", "status": "active" }'
```

A connection becomes `active` only once the latest test login of its current configuration
has passed (`409` otherwise). Changing the issuer, client ID, scopes or claims later clears
the test result and puts an active connection back to `draft`: test it again, then activate
it again. Saving the scopes in another order changes nothing.

Rotating the client secret is not a new configuration: send the new `client_secret` and the
connection stays `active`. A wrong secret shows as failed sign-ins, so run a test login after
rotating.

To turn single sign-on off, select **Disable** (save with `"status": "disabled"`), or
**Delete connection** (`DELETE /v1/organizations/{id}/sso/oidc`). Every change to the
connection is recorded in the organization's audit log, and a deletion is recorded at high
severity.

## Signing in

Once the connection is active, the sign-in page asks for the email first. People at your
verified domains enter their email and are sent straight to your IdP; any other address
continues with Google or GitHub as before.

* **MFA is your IdP's.** An SSO sign-in does not ask for a Qumo two-factor code: the IdP's
  own policies (MFA, device checks, conditional access) apply instead. To have Qumo refuse
  sign-ins your IdP reports no MFA for, set an [MFA policy](https://docs.qumo-deploy.com/guides/sso/enforcement.md#mfa-policy).
* **First sign-in.** The first SSO sign-in of someone without a Qumo account creates a
  managed account for them, managed by your organization and with no personal organization
  of its own.
* If the sign-in cannot complete (for example the IdP refuses it, or the email the IdP
  returns is not at one of your verified domains), the person is sent back to the sign-in
  page with the reason.

## Enforce it

An active connection lets people sign in with your IdP but does not stop them using Google or
GitHub. To require SSO for your managed accounts, require MFA at the IdP, and keep a
break-glass administrator for when the IdP is down, see
[Enforce Single Sign-On](https://docs.qumo-deploy.com/guides/sso/enforcement.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.