# Single sign-on (SSO) and SCIM

Single sign-on lets an organisation's members enter the console through their company's identity provider, and SCIM lets the company directory create and deactivate their accounts. This page says what works today, what is still missing, and from which tier the feature is planned.

## Where the feature stands

| Item | Status |
| --- | --- |
| Console sign-in through an OpenID Connect provider | Served: the "Se connecter avec l'authentification unique" (sign in with single sign-on) button on the sign-in page. |
| SCIM 2.0 account provisioning (`/scim/v2/Users`) | Served. |
| Provider configuration and SCIM token issuance | **Not yet available as self-service**: there is no screen, and the `sso.configure` operation is declared but not served (the route returns 404). |
| SAML | **Not available.** It will go through a separate service, never through SAML written inside the platform. |
| Mapping directory groups to roles | Not available. |

The price list places single sign-on and SCIM in the **Standard**, **Pro** and **Souverain** tiers; they are not planned for the Découverte tier. See [Billing](billing.md).

> **Attention.** This page is about your teams entering the Bastide console. Signing in your application's users with Google, Microsoft Entra ID, Keycloak and other providers is a different feature: see [Authentication](auth.md).

## How a sign-in goes

1. On the console sign-in page, the person picks "Se connecter avec l'authentification unique" and types their organisation's short identifier (slug).
2. The console sends them to the organisation's identity provider.
3. The provider sends them back to `https://console.bastide.app/connexion/sso`, the redirect address to register with the provider.
4. The platform checks the response and opens a console session, or asks for the second factor first.

The rules protecting this path:

- **Only an account provisioned by the organisation's SCIM directory can open a session this way.** A provider, even a misconfigured one, can therefore never take over the account of an address registered elsewhere on the platform.
- On the first sign-in, the provider's identifier (`sub`) is bound to the account; from then on it designates the account. An address renamed at the provider does not move the identity.
- **The Bastide second factor is still required.** An account holding one gives it after the provider, as after a password. See [Security](security.md).
- A suspended account, whether by the directory or by operations, opens nothing.

## What the platform checks

- Authorisation code flow with **PKCE `S256`**, as a public client: no client secret is stored.
- Requested scopes: `openid email profile`.
- The `state` parameter lives **10 minutes** and can be used once.
- The ID token must be signed with **RS256** or **ES256** by a key the provider publishes; issuer, audience, expiry and `nonce` are checked. Any other algorithm is refused.
- The issuer address is checked like any outgoing address, and its OpenID Connect discovery must answer: a configuration that leads to no provider is refused straight away.

Any OpenID Connect provider with discovery will do: Keycloak, Microsoft Entra ID, Okta, Google Workspace.

## The two sign-in calls

For a tool that drives the sign-in itself, the console uses two API routes outside the operation catalogue:

```sh
curl -X POST https://api.bastide.app/v1/account/sso/start \
  -H "Content-Type: application/json" \
  -d '{ "organization": "my-organisation" }'
```

The response carries `authorizationUrl`, where to send the person, and `expiresAt`. On return, the `/connexion/sso` page posts the code and state it received:

```json
{ "code": "…", "state": "…" }
```

to `POST /v1/account/sso/complete`. The response is that of an ordinary sign-in: a session, or the second-factor challenge.

## SCIM: create and deactivate accounts from the directory

The company directory talks to `https://api.bastide.app/scim/v2/` with a bearer token specific to the organisation. That token opens these routes only, and lives at most 90 days.

| Route | Effect |
| --- | --- |
| `GET /scim/v2/ServiceProviderConfig` | What the service accepts: `PATCH` and filtering yes (200 results at most), no bulk, no sorting, no password change. |
| `GET /scim/v2/Users` | The accounts this directory created, filterable. |
| `POST /scim/v2/Users` | Creates an account. An address that already has an account on the platform is refused (409). |
| `GET`, `PUT`, `PATCH /scim/v2/Users/{id}` | Reads or updates an account; `active: false` deactivates it. |
| `DELETE /scim/v2/Users/{id}` | Deactivates the account without erasing it: its history stays attached. |

A deactivation takes effect on the request itself: the account is suspended, its console sessions are closed, and its personal tokens are refused while it stays suspended. Reactivating the account only lifts the suspension the directory set.

Provisioning creates the account; it gives it no role. Roles are then given as for any account, by invitation (see [Organisations and members](organizations.md)).

## Common errors

| Code | Status | What happens | What to do |
| --- | --- | --- | --- |
| `sso_indisponible` | 404 | The organisation has no active single sign-on, its provider does not answer, or the identifier typed is unknown. The response is the same in all three cases. | Check the organisation's short identifier; otherwise sign in with a password. |
| `sso_refuse` | 401 | The provider's return was refused: expired or already used state, invalid ID token, or no active account provisioned by the directory. | Start again from the sign-in page; if it persists, check that the directory did create your account. |
| `trop_de_tentatives` | 429 | Too many failures from your IP address. | Wait for the time given by the `Retry-After` header. |
| (SCIM) | 401 | SCIM token missing, unknown, expired or revoked; the response does not say which. | Replace the token in the directory. |

See also: [Security](security.md), [Organisations and members](organizations.md), [Audit log](audit.md), [Authentication](auth.md).
