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.
Register a receiver
Section titled “Register a receiver”A receiver is a confidential client that signs in as itself.
- In
/manage, add a client with theclient_credentialsgrant. - Add
masks:signalsto its allowed scopes. - The receiver requests a token with
grant_type=client_credentialsandscope=masks:signals, and sends it as a bearer token to every endpoint below.
A token issued to a person cannot manage a stream.
Find the endpoints
Section titled “Find the endpoints”/.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 |
Create a stream
Section titled “Create a stream”A receiver holds one stream. POST /ssf/streams creates it.
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.
What is sent
Section titled “What is sent”| 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.
Verify a delivery
Section titled “Verify a delivery”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.
Failures
Section titled “Failures”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.
Receiving signals from a provider
Section titled “Receiving signals from a provider”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/eventsThe provider posts each event as an application/secevent+jwt body. masks accepts it with 202 when:
issis the provider’s issuer, and the provider accepts security events.- It is signed by a key in the provider’s
jwks_uriand typedsecevent+jwt. audis the tenant’s issuer or the endpoint above.- Its
jtihas 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.