# Connexion unique (OpenID Connect)

> Ce que Moon AI attend de votre fournisseur OpenID Connect, le déroulé d’une connexion, les champs lus et les routes qui lisent ou enregistrent la configuration.

La connexion unique d’une organisation gérée utilise OpenID Connect : vos membres se connectent à Moon AI avec l’identifiant de votre organisation (Microsoft Entra ID, Google Workspace, Okta, Keycloak…). La mise en place depuis l’administration est décrite dans [Connexion unique](https://realmoon.ai/documentation/organisations/sso/).

## Ce que doit offrir votre fournisseur

| Élément | Attendu |
| --- | --- |
| Découverte | Un document `/.well-known/openid-configuration` sous l’émetteur, en HTTPS, dont `issuer` est l’émetteur lui-même. Ses adresses d’autorisation, de jeton et de clés sont en HTTPS. |
| Flux | Code d’autorisation avec PKCE (`S256`). |
| Application | Un identifiant client et, en général, un secret client. Sans secret, aucune authentification du client n’est envoyée. |
| Adresse de redirection | `https://chat.realmoon.ai/api/v1/orgs/sso/callback` |
| Authentification au jeton | `client_secret_basic` si le fournisseur l’annonce (ou n’annonce rien), sinon `client_secret_post`. |
| Signature du jeton d’identité | RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 ou ES512, avec une clé RSA ou EC publiée. `none` et les HMAC (`HS…`) sont refusés. |
| Permissions demandées | `openid email profile` par défaut ; `openid` est toujours ajouté. |

## Déroulé d’une connexion

1. Le membre choisit « Continuer avec l’identifiant de mon organisation » et saisit son adresse, ou ouvre le lien de connexion de l’organisation : `https://chat.realmoon.ai/auth?org_sso=<org_id>`.
2. Moon AI redirige vers votre fournisseur avec un `state` et un `nonce` à usage unique, valables 10 minutes et liés au navigateur, et le défi PKCE. L’adresse saisie est transmise en `login_hint`.
3. Votre fournisseur renvoie vers l’adresse de redirection. Moon AI échange le code, puis vérifie le jeton d’identité : signature, `iss`, `aud`, `azp` (quand `aud` a plusieurs valeurs), `exp` et `iat` (60 secondes de tolérance), `sub` et `nonce`.
4. Moon AI lit l’adresse dans le champ choisi (`email` par défaut) et exige `email_verified` à vrai, sauf avec l’option « Faire confiance aux adresses de ce fournisseur ». Pour un émetteur Microsoft Entra ID propre à votre tenant (`https://login.microsoftonline.com/<id du tenant>/v2.0`), une adresse sans `email_verified` est acceptée.
5. Le compte est ouvert ou créé selon les règles de l’organisation, puis la double authentification du compte Moon AI est demandée si elle est activée.

Une adresse lue dans un autre champ que `email` n’est considérée comme vérifiée qu’avec l’option « Faire confiance aux adresses de ce fournisseur ».

## Qui la connexion peut ouvrir

- Sans domaine vérifié : les membres actifs ou suspendus de l’organisation (un membre suspendu le reste) et les personnes invitées comme membres.
- Avec un [domaine vérifié](https://realmoon.ai/documentation/api-organisations/domaines/), ou un émetteur servi sur votre domaine : toute adresse de ce domaine ; le compte est créé s’il n’existe pas. Sur un workspace payé par carte, ou sans siège libre, la personne est connectée sans être ajoutée à l’organisation.
- Jamais le compte du propriétaire de l’organisation, ni celui d’un administrateur de la plateforme.
- Jamais une adresse d’un domaine vérifié par une autre organisation, ni d’une messagerie publique.

Quand la connexion est obligatoire, le propriétaire et les administrateurs de l’organisation gardent leurs autres moyens de connexion, pour ne pas perdre l’accès si le fournisseur tombe en panne. Au moment où elle devient obligatoire, les sessions de tous les comptes des domaines vérifiés sont fermées, sauf celles du propriétaire.

## Champs lus

| Réglage | Par défaut | Rôle |
| --- | --- | --- |
| `claim_email` | `email` | L’adresse du membre. |
| `claim_name` | `name` | Le nom affiché ; à défaut, `given_name` et `family_name`, puis le début de l’adresse. |
| `claim_groups` | aucun | Les groupes du membre chez le fournisseur : une chaîne ou une liste (500 au plus). |
| `group_map` | `{}` | Groupes du fournisseur vers groupes de l’organisation. |

`group_map` associe le nom ou l’identifiant d’un groupe du fournisseur à un ou plusieurs groupes de l’organisation : 200 entrées au plus, 50 groupes par entrée. Les groupes visés sont des groupes statiques, ni venus de l’annuaire, ni donnant le rôle d’administrateur. À chaque connexion, avec `claim_groups` réglé, le membre rejoint les groupes de ses groupes de fournisseur et quitte ceux que la connexion unique lui avait donnés ; les ajouts faits à la main ne sont jamais retirés.

```json
{
  "Enseignants-Lycee": ["<id du groupe Enseignants>"],
  "Eleves-Seconde": ["<id du groupe Secondes>", "<id du groupe Élèves>"]
}
```

## Lire la configuration par l’API

`GET /api/v1/orgs/{org_id}/sso` · `security:read`

```bash title="cURL"
curl https://chat.realmoon.ai/api/v1/orgs/$ORG_ID/sso \
  -H "Authorization: Bearer $MOONAI_ADMIN_KEY"
```

```python title="Python"
import os
import requests

r = requests.get(
    f"https://chat.realmoon.ai/api/v1/orgs/{os.environ['ORG_ID']}/sso",
    headers={"Authorization": f"Bearer {os.environ['MOONAI_ADMIN_KEY']}"},
    timeout=30,
)
r.raise_for_status()
sso = r.json()
print(sso["sso"]["status"] if sso["sso"] else "non configurée", sso["sign_in_url"])
```

```javascript title="JavaScript"
const r = await fetch(`https://chat.realmoon.ai/api/v1/orgs/${process.env.ORG_ID}/sso`, {
  headers: { Authorization: `Bearer ${process.env.MOONAI_ADMIN_KEY}` },
});
if (!r.ok) throw new Error(await r.text());
const { sso, sign_in_url } = await r.json();
console.log(sso ? sso.status : 'non configurée', sign_in_url);
```

```json
{
  "sso": {
    "id": "8b0e2c4a-6d8f-4a1b-9c3e-5f7a9b1d3e5f",
    "org_id": "8c1f2a4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
    "issuer": "https://login.microsoftonline.com/<id du tenant>/v2.0",
    "client_id": "<identifiant client>",
    "has_secret": true,
    "scopes": "openid email profile",
    "claim_email": "email",
    "claim_name": "name",
    "claim_groups": "groups",
    "group_map": { "Enseignants": ["<id de groupe>"] },
    "enforced": false,
    "trust_email": false,
    "status": "active",
    "provider_metadata": { "issuer": "https://login.microsoftonline.com/<id du tenant>/v2.0", "key_count": 3 },
    "tested_at": 1762339200,
    "last_error": null,
    "created_at": 1762000000,
    "updated_at": 1762339200
  },
  "redirect_uri": "https://chat.realmoon.ai/api/v1/orgs/sso/callback",
  "verified_domains": ["ecole.fr"],
  "sign_in_url": "https://chat.realmoon.ai/auth?org_sso=8c1f2a4e-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
  "can_configure": false
}
```

`sso` vaut `null` sans configuration ; `status` vaut `draft`, `active` ou `disabled`. Le secret n’est jamais rendu (`has_secret`).

## Enregistrer la configuration

Configurer, tester et retirer la connexion unique donne accès aux comptes des membres : seuls le propriétaire et les administrateurs de l’organisation le font, depuis l’administration › « Connexion » ou avec leur session. Une clé d’administration reçoit `403 owner_only` sur ces routes (`can_configure: false`).

| Méthode et chemin | Rôle |
| --- | --- |
| `PUT /api/v1/orgs/{org_id}/sso` | Enregistrer toute la configuration. |
| `POST /api/v1/orgs/{org_id}/sso/test` | Tester un émetteur, sans rien activer. |
| `DELETE /api/v1/orgs/{org_id}/sso` | Retirer la connexion et son secret. |

Corps de `PUT /sso` : il remplace toute la configuration ; un champ absent reprend sa valeur par défaut.

| Champ | Type | Obligatoire | Description |
| --- | --- | --- | --- |
| `issuer` | chaîne | oui | L’émetteur, en `https://`, sans paramètres (8 à 512 caractères). |
| `client_id` | chaîne | oui | L’identifiant client, sans espace. |
| `client_secret` | chaîne | non | Le secret ; absent ou vide, le secret enregistré est gardé. |
| `scopes` | chaîne | non | Permissions séparées par des espaces, 20 au plus. |
| `claim_email`, `claim_name`, `claim_groups` | chaîne | non | Les [champs lus](https://realmoon.ai/documentation/api-organisations/sso/#champs). |
| `group_map` | objet | non | Absent : la correspondance est vidée. |
| `enabled` | booléen | non | `false` par défaut : un `PUT` sans `enabled` désactive la connexion. Avec `true`, le fournisseur est testé avant l’activation. |
| `enforced` | booléen | non | Connexion obligatoire pour les comptes des domaines vérifiés. Demande `enabled` et au moins un domaine vérifié. |
| `trust_email` | booléen | non | Faire confiance aux adresses du fournisseur ; absent, le choix enregistré est gardé. |

Changer `issuer` ou `client_id` remet la configuration en brouillon, efface le secret (sauf s’il est envoyé dans la même requête) et les liens des membres avec l’ancien fournisseur : un second `PUT` avec `enabled: true` l’active. Chaque changement est signalé au propriétaire de l’organisation.

| Statut | `code` | Cause |
| --- | --- | --- |
| 400 | `invalid_request` | Champ invalide (`fields`), `enforced` sans `enabled`. |
| 400 | `sso_test_failed` | Avec `enabled: true`, le fournisseur ne répond pas comme attendu ; `reason` donne la cause. |
| 403 | `owner_only` | Ni propriétaire ni administrateur de l’organisation, ou une clé d’administration. |
| 409 | `sso_enforce_needs_domain` | Connexion obligatoire sans domaine vérifié. |
| 409 | `directory_group_admin` | `group_map` vise un groupe qui donne le rôle d’administrateur. |

`POST /sso/test` reçoit `{"issuer": "…"}` (facultatif : l’émetteur enregistré sinon) et répond `200` avec `{"ok": true, "issuer", "authorization_endpoint", "token_endpoint", "jwks_uri", "key_count"}`, ou `{"ok": false, "reason"}`. Il est limité à 20 essais par organisation en 10 minutes.

| `reason` | Cause |
| --- | --- |
| `issuer_invalid` | Adresse qui n’est pas en `https://`, ou avec des identifiants ou des paramètres. |
| `provider_unreachable` | Pas de réponse en 8 secondes, réponse trop grande ou illisible, adresse refusée. |
| `provider_http_error` | Le document de découverte ou les clés ne répondent pas `200`. |
| `discovery_invalid` | Document incomplet, ou adresses qui ne sont pas en HTTPS. |
| `issuer_mismatch` | L’`issuer` du document diffère de l’adresse testée. |
| `jwks_invalid` | Aucune clé de signature RSA ou EC utilisable. |

## Erreurs vues par le membre

Le membre voit un message en clair ; les plus fréquents et leur cause sont listés dans [Connexion unique](https://realmoon.ai/documentation/organisations/sso/). Le démarrage d’une connexion est limité à 30 essais par adresse IP en 10 minutes.

## Voir aussi

- [Domaines](https://realmoon.ai/documentation/api-organisations/domaines/)
- [SCIM 2.0](https://realmoon.ai/documentation/api-organisations/scim/)
- [Comptes des membres](https://realmoon.ai/documentation/api-organisations/comptes/#exigences)

---

Documentation Moon AI › API des organisations › Connexion unique (SSO). Page : https://realmoon.ai/documentation/api-organisations/sso/. Modifiée le 5 novembre 2026.

© 2026 Stellarr Studio SAS. Tous droits réservés. Moon AI, Moon Blur et Stellarr Studio sont des marques déposées à l’INPI.
