Skip to content

Shared signals

masks is a Shared Signals Framework (SSF) transmitter. A receiver registers a stream, and masks pushes it a signed security event token when a session is revoked, a credential changes, or an account is suspended, so the receiver can act before the affected tokens expire. It is also a receiver: a provider people sign in with can tell masks to sign someone out. See receiving signals from a provider.

A receiver is a confidential client that signs in as itself.

  1. In /manage, add a client with the client_credentials grant.
  2. Add masks:signals to its allowed scopes.
  3. The receiver requests a token with grant_type=client_credentials and scope=masks:signals, and sends it as a bearer token to every endpoint below.

A token issued to a person cannot manage a stream.

/.well-known/ssf-configuration lists the endpoints and the delivery methods.

Field Value
issuer The tenant’s issuer.
jwks_uri The keys that verify each security event token.
delivery_methods_supported urn:ietf:rfc:8935, push delivery.
configuration_endpoint /ssf/streams
status_endpoint /ssf/status
verification_endpoint /ssf/verify
default_subjects ALL

A receiver holds one stream. POST /ssf/streams creates it.

Terminal window
curl -s "$MASKS/ssf/streams" -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"delivery": {
"method": "urn:ietf:rfc:8935",
"endpoint_url": "https://receiver.example.com/events",
"authorization_header": "Bearer inbound-secret"
},
"events_requested": [
"https://schemas.openid.net/secevent/caep/event-type/session-revoked",
"https://schemas.openid.net/secevent/caep/event-type/credential-change"
]
}'

The response holds stream_id, iss, aud, events_supported, events_requested, and events_delivered. The endpoint must be an HTTPS address on a public network. masks sends authorization_header as the Authorization header of each delivery, stores it encrypted, and never returns it.

Request Effect
GET /ssf/streams Lists the receiver’s stream, or returns it when stream_id is given.
PATCH /ssf/streams Changes the fields in the body.
PUT /ssf/streams Replaces the delivery, events, and description.
DELETE /ssf/streams?stream_id= Removes the stream.
GET /ssf/status?stream_id= Returns enabled, paused, or disabled.
POST /ssf/status Sets the status, with an optional reason.
POST /ssf/verify Sends a verification event carrying the state in the body.

A paused or disabled stream delivers nothing, and events raised while it is paused are dropped.

Event type Sent when
CAEP session-revoked A manager revokes a session or signs a person out everywhere, a person leaves or is removed from an organization, or their organization is archived.
CAEP credential-change A password changes or is reset, or a passkey or authenticator app is added or removed.
CAEP token-claims-change A person’s role in an organization changes. The event’s claims carry the new org claim.
RISC account-disabled An account is suspended.
RISC account-enabled A suspended account is restored.

A stream receives events only about people who have signed in to its client or consented to it. An organization’s events go only to a client that has held a token for that organization. The subject is {"format": "iss_sub", "iss": ..., "sub": ...}, where sub is the same subject the client sees in ID tokens, including a pairwise one.

Each delivery is a POST with the content type application/secevent+jwt and a JWT as the body. Its header has typ set to secevent+jwt, and it is signed with the tenant’s key from jwks_uri. Check that iss is the issuer and aud is the receiver’s client_id, and use jti to discard a repeat.

A CAEP event carries event_timestamp and initiating_entity, which is user when the person acted, admin when a manager did, and system otherwise.

A delivery that gets no 2xx response is attempted up to eight times, with growing delays. After the last attempt, masks records a signal.undelivered event. Removing masks:signals from the client, or archiving the client, stops delivery.

masks also receives security events from an OpenID Connect provider people sign in with. Turn on Accept its security events for the provider in /manage, and give the provider this receiver endpoint for push delivery (RFC 8935):

https://acme.auth.example.com/ssf/events

The provider posts each event as an application/secevent+jwt body. masks accepts it with 202 when:

  • iss is the provider’s issuer, and the provider accepts security events.
  • It is signed by a key in the provider’s jwks_uri and typed secevent+jwt.
  • aud is the tenant’s issuer or the endpoint above.
  • Its jti has not been seen in the last seven days.

Otherwise masks answers 400 with err set to invalid_issuer, invalid_key, invalid_audience, or invalid_request, and a description.

These events sign the account out everywhere: every session, every refresh token, and every trusted device. Each is recorded as a signal.received event on the account.

Event
CAEP session-revoked A session ended at the provider.
CAEP credential-change A password or factor changed at the provider.
RISC sessions-revoked Every session ended at the provider.
RISC account-disabled The account was disabled at the provider.
RISC account-purged The account was deleted at the provider.
RISC account-credential-change-required The provider asks for new credentials.

The account is the one connected to the provider with that subject. masks reads a subject in the iss_sub format, whose iss must be the provider’s issuer, or in the email format, which matches the address the connection holds. Other event types and subjects are accepted and change nothing.