OAuth2 & OIDC

Signaux partagés (SSF)

Sur cette page

Quand un utilisateur est désactivé ou supprimé, TOSIAM ferme ses sessions et révoque ses jetons (voir Révocation à la désactivation). Une application qui garde sa propre session, ou qui valide les jetons par leur seule signature, n’en sait rien : elle accepte l’utilisateur jusqu’à l’expiration.

À partir de la version 3.38.0, TOSIAM peut la prévenir. Il est émetteur du Shared Signals Framework (SSF 1.0) : il pousse à chaque application abonnée, le récepteur, un jeton d’événement de sécurité signé (SET, RFC 8417), ou le lui garde jusqu’à ce qu’elle le relève.

Faites défiler le tableau
ChangementÉvénementsCanaux
Utilisateur désactivéRISC account-disabled et CAEP session-revokedTous
Utilisateur suppriméRISC account-purged et CAEP session-revokedTous
Utilisateur réactivéRISC account-enabledÉcriture par TOSIAM seulement
Clé d’accès supprimée par l’utilisateur ou un administrateurCAEP credential-changeConsole, API, libre-service

Une désactivation ou une suppression est signalée quel que soit le canal du changement : console, API REST, ssoadm, TOSIDM ou écriture directe dans l’annuaire (voir ce qui déclenche la révocation). Une réactivation n’est connue que si TOSIAM l’écrit lui-même : rien ne garde l’état précédent d’un compte réactivé dans l’annuaire. Les clés d’accès d’un utilisateur supprimé ne donnent pas de credential-change : sa suppression est déjà signalée.

1. Le récepteur#

Un récepteur est un client OAuth2 du royaume. Il gère son flux d’événements avec un jeton d’accès :

  • obtenu pour lui-même, par le grant client_credentials : un jeton délivré pour un utilisateur est refusé ;
  • portant la portée ssf.manage, à ajouter aux portées du client.

Un récepteur a un flux. Il ne voit ni ne modifie celui d’un autre récepteur.

bash
curl -s -u crm:secret -d grant_type=client_credentials -d scope=ssf.manage \
  https://am.example.com/tosiam/oauth2/ventes/access_token

2. Découverte#

La configuration de l’émetteur est publiée à côté de celle d’OpenID Connect, sans authentification :

texte
GET /tosiam/oauth2/<royaume>/.well-known/ssf-configuration
json
{
  "spec_version": "1_0",
  "issuer": "https://am.example.com/tosiam/oauth2/ventes",
  "jwks_uri": "https://am.example.com/tosiam/oauth2/ventes/connect/jwk_uri",
  "delivery_methods_supported": ["urn:ietf:rfc:8935", "urn:ietf:rfc:8936"],
  "configuration_endpoint": "https://am.example.com/tosiam/oauth2/ventes/ssf/stream",
  "status_endpoint": "https://am.example.com/tosiam/oauth2/ventes/ssf/status",
  "verification_endpoint": "https://am.example.com/tosiam/oauth2/ventes/ssf/verify",
  "add_subject_endpoint": "https://am.example.com/tosiam/oauth2/ventes/ssf/subjects/add",
  "remove_subject_endpoint": "https://am.example.com/tosiam/oauth2/ventes/ssf/subjects/remove",
  "authorization_schemes": [{"spec_urn": "urn:ietf:rfc:6749"}],
  "default_subjects": "ALL"
}

Par défaut, un flux porte sur tous les utilisateurs du royaume (default_subjects: ALL). Un récepteur peut le restreindre (voir Sujets).

3. Gérer son flux#

Faites défiler le tableau
RequêteRôle
POST /ssf/streamCréer le flux. Répond 409 si le récepteur en a déjà un.
GET /ssf/streamLister les flux du récepteur (zéro ou un). Avec ?stream_id=, lire le flux.
PATCH /ssf/streamChanger les champs donnés.
PUT /ssf/streamRemplacer les réglages : un champ absent reprend sa valeur par défaut.
DELETE /ssf/stream?stream_id=Supprimer le flux et les événements en attente.
GET /ssf/status?stream_id=, POST /ssf/statusLire ou changer le statut.
POST /ssf/verifyDemander un événement de vérification.
POST /ssf/subjects/add, POST /ssf/subjects/removeAjouter ou retirer un sujet.
POST /ssf/pollRelever les événements d’un flux en relève.

Création :

bash
curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://am.example.com/tosiam/oauth2/ventes/ssf/stream -d '{
    "delivery": {
      "method": "urn:ietf:rfc:8935",
      "endpoint_url": "https://crm.example.com/ssf/events",
      "authorization_header": "Bearer jeton-du-crm"
    },
    "events_requested": [
      "https://schemas.openid.net/secevent/risc/event-type/account-disabled",
      "https://schemas.openid.net/secevent/risc/event-type/account-purged",
      "https://schemas.openid.net/secevent/caep/event-type/session-revoked"
    ],
    "description": "CRM"
  }'
  • delivery : poussée (urn:ietf:rfc:8935) ou relève (urn:ietf:rfc:8936).
    • Pour une poussée, l’endpoint_url doit être en https, sans utilisateur ni fragment. L’authorization_header est envoyé tel quel à chaque poussée. Il est gardé chiffré et n’est jamais renvoyé.
    • Pour une relève, le récepteur ne donne pas d’endpoint_url : la réponse lui donne celui de TOSIAM, /ssf/poll.
    • Changer de méthode déplace les événements en attente vers la nouvelle.
  • events_requested : sans ce champ, tous les événements pris en charge sont livrés. La réponse donne events_delivered, les événements demandés que TOSIAM prend en charge.
  • default_subjects : ALL (par défaut) ou NONE. SSF 1.0 définit ce champ pour l’émetteur entier ; TOSIAM laisse chaque récepteur le choisir pour son flux.

Sujets#

Un flux ALL porte sur tous les utilisateurs du royaume, sauf ceux que le récepteur a retirés. Un flux NONE ne porte sur aucun, sauf ceux qu’il a ajoutés.

bash
curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://am.example.com/tosiam/oauth2/ventes/ssf/subjects/add -d '{
    "stream_id": "8c1f...",
    "subject": {"format": "iss_sub", "iss": "https://am.example.com/tosiam/oauth2/ventes", "sub": "jdupont"}
  }'
  • Le sujet est au format iss_sub, avec l’issuer du flux et le sub que le récepteur connaît, pairwise compris. Les autres formats sont refusés (400).
  • Un flux liste 1 000 sujets au plus.
  • Changer default_subjects abandonne les sujets ajoutés ou retirés : ils voudraient dire le contraire.

La spécification OpenAPI de ces points est publiée sous le nom ssf, comme celle d’OpenID Connect.

4. Les événements#

Chaque événement est un JWT de type secevent+jwt, signé par les clés OAuth2 du royaume, celles du jwks_uri : le récepteur le vérifie comme un id_token. Un royaume qui signe ses jetons par secret partagé (HS256) signe ses événements en RS256.

json
{
  "iss": "https://am.example.com/tosiam/oauth2/ventes",
  "jti": "3c3c4f0e-8a6b-4f2d-9f43-0d8f1b3a2c11",
  "iat": 1761696000,
  "aud": ["crm"],
  "txn": "b7c1d2e3-0f45-4a6b-8c9d-0e1f2a3b4c5d",
  "sub_id": {
    "format": "iss_sub",
    "iss": "https://am.example.com/tosiam/oauth2/ventes",
    "sub": "jdupont"
  },
  "events": {
    "https://schemas.openid.net/secevent/caep/event-type/session-revoked": {
      "event_timestamp": 1761696000,
      "initiating_entity": "admin",
      "reason_admin": {"en": "The account was disabled"}
    }
  }
}
  • sub_id : le sub que le client connaît, celui de ses id_tokens. Il est pairwise si le client le demande.
  • txn : commun aux événements d’un même changement. Quand TOSIAM a écrit le changement, c’est la transaction de la requête, qu’on retrouve dans l’audit.
  • initiating_entity : admin quand le changement a été écrit par TOSIAM (console, API, ssoadm), system quand il a été appris de l’annuaire (TOSIDM, écriture directe).
  • account-disabled, account-enabled et account-purged ne portent pas de champ.
  • credential-change porte credential_type (fido2-platform, ou fido2-roaming quand le rattachement de la clé n’est pas connu), change_type (delete), friendly_name et fido2_aaguid quand ils sont connus, initiating_entity (user ou admin) et event_timestamp.

Une désactivation ou une suppression est signalée une seule fois pour tout le site : chaque serveur reçoit la notification de l’annuaire, et un seul d’entre eux émet, élu par une réservation dans le Core Token Store. Le serveur qui a écrit le changement passe en premier, car il en connaît l’auteur et la transaction : les autres attendent 5 secondes avant de tenter leur chance. Un changement fait hors de TOSIAM est donc signalé environ 5 secondes après sa notification. Quand TOSIAM apprend seulement qu’un utilisateur déjà inactif a été modifié, il ne le signale de nouveau qu’au bout d’un jour. Une réactivation par TOSIAM lève cette retenue : une nouvelle désactivation est signalée aussitôt. Une réactivation et une suppression de clé d’accès ne sont connues que du serveur qui les écrit, qui les signale seul.

5. Livraison#

Les événements attendent dans le Core Token Store : n’importe quel serveur du site les livre, même si celui qui les a émis s’arrête.

texte
POST https://crm.example.com/ssf/events
Content-Type: application/secevent+jwt
Accept: application/json
Authorization: Bearer jeton-du-crm

eyJ0eXAiOiJzZWNldmVudCtqd3QiLCJraWQiOi...
Faites défiler le tableau
Réponse du récepteurSuite
2xx (202 attendu)Livré, supprimé de la file
400, 413, 415Refusé : abandonné
Autre réponse, ou récepteur injoignableNouvel essai après 2 s, puis un délai doublé à chaque essai, au plus 5 min ; abandon après 10 essais

Un événement non livré au bout de 24 heures est abandonné. Les redirections ne sont pas suivies.

Relève#

Un flux en relève (RFC 8936) garde ses événements jusqu’à ce que le récepteur les acquitte :

bash
curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  https://am.example.com/tosiam/oauth2/ventes/ssf/poll -d '{
    "ack": ["3c3c4f0e-8a6b-4f2d-9f43-0d8f1b3a2c11"],
    "maxEvents": 10,
    "returnImmediately": false
  }'
json
{
  "sets": {"5d1e...": "eyJ0eXAiOiJzZWNldmVudCtqd3QiLCJraWQiOi..."},
  "moreAvailable": false
}
  • ack : les jti des événements reçus, supprimés de la file. setErrs : ceux que le récepteur refuse, par jti, supprimés aussi et tracés dans les journaux.
  • maxEvents : 20 par défaut, 100 au plus. Les événements sont rendus du plus ancien au plus récent, et rendus de nouveau tant qu’ils ne sont pas acquittés.
  • Sans returnImmediately, la requête attend un événement jusqu’à 5 secondes.
  • Un flux en pause ne rend rien. Un événement non relevé au bout de 24 heures est abandonné.

Vérification et statut#

  • Vérification : POST /ssf/verify avec {"stream_id": "...", "state": "..."}. TOSIAM pousse un événement verification qui renvoie le state, au sujet du flux lui-même ({"format": "opaque", "id": "<stream_id>"}).
  • Statut : POST /ssf/status avec {"stream_id": "...", "status": "paused"}.
Faites défiler le tableau
StatutEffet
enabledLes événements sont livrés.
pausedLes événements sont gardés, et livrés dans la minute qui suit la réactivation, s’ils ont moins de 24 heures.
disabledLes événements sont abandonnés, y compris ceux en attente.

6. Réglage et suivi#

Faites défiler le tableau
Propriété serveurValeur par défautRôle
org.tst.tosiam.ssf.allowInsecureEndpointsfalseAccepte un endpoint_url en http. Réservée aux tests.

Sans flux dans un royaume, une révocation n’ajoute aucune écriture au Core Token Store.

Les journaux sont écrits par la catégorie OAuth2Provider :

texte
SSF stream 8c1f... of crm created in the realm /ventes
2 SSF event(s) of jdupont emitted, txn b7c1d2e3-...
SSF event 3c3c4f0e-... delivered to crm
SSF event 3c3c4f0e-... not delivered to crm, attempt 1: ConnectException: ...
SSF event 3c3c4f0e-... for crm given up after 10 attempt(s): 503 ...

7. Limites de cette version#

  • account-enabled n’est émis que pour une réactivation écrite par TOSIAM.
  • credential-change n’est émis que pour les clés d’accès, pas pour les appareils OATH ou push, ni pour le mot de passe.
  • session-revoked n’est émis qu’à la désactivation ou à la suppression d’un utilisateur, pas à une déconnexion ordinaire.
  • Les sujets sont au seul format iss_sub.

Mis à jour le