Events¶
The core, beffroi/oidc and beffroi/saml dispatch PSR-14 events, and the bundle replays them on Messenger. Every
event names its act, the date it happened, its tenant and the provenance of the request it came from, as the
events of the core describe; the tables below say what each one
carries beyond those four, and who it concerns:
- a person, when it implements
ConcernsAPersonInterfaceand names the subject of their account, never thesuba client was given; - a client, when it implements
ConcernsAClientInterfaceand names the client by the identifier it was registered under, never the client itself.
Every event crosses the broker¶
An event is routed to the transport async_priority_low, the database unless the deployment names a broker,
every one of them by the interface they all implement, so an event added to a package leaves the process without
a line of configuration. Its body is a JWE to the message key of the tenant; what stays readable on the transport
is the class of the event, its stamps and the slug of its tenant. Nothing listens in the process that raised it,
and an event nothing listens to is announced all the same.
What listens¶
Every event is written to the journal. WriteTheActDownHandler hears every event of the three packages and
writes one row: the act, the date, the provenance, sealed, the subject when the event concerns a person and the
client when it concerns a client, and nothing else. A row is kept beffroi.journal.retention days, 180 by default,
and beffroi:user:journal reads it back for a person.
The other listeners of the bundle each hear one event, or an interface a few of them implement:
| Listener | Hears | What it does |
|---|---|---|
BackChannelLogoutHandler |
session.ended |
Mints a logout token for each client of the session that registered a back-channel logout URI (Back-Channel Logout 1.0 §2.6), each one raising logout_token.issued |
RevokeTheTokensOfTheSessionHandler |
session.ended |
Revokes the refresh tokens of the session and removes its opaque access tokens, dated by the end of the session |
RevokeWhatOutlivedTheSessionHandler |
refresh_token.outlived_its_session |
The same again, dated by the end of the session, for what a refresh in flight had saved |
EnrichTheProvenanceHandler |
user.authenticated, through SessionProvenanceInterface |
Finds the place of the two provenances of the session and writes it on its row |
BuildThePersonalArchiveHandler |
archive.asked |
Builds the archive the person asked for |
RecordClientUseHandler |
authorization_code.issued and tokens.issued, through ClientInUseInterface |
Dates the last use of the client, which the removal of the clients nobody uses reads |
WriteWhatTheClientWasGivenHandler |
tokens.issued |
Writes, beside the act, what the client was given of the person, kept as long as the act |
QueueLogoutTokenHandler |
logout_token.issued |
Hands the token to the notifications lane, async_notifications, which posts it to the client |
QueueClientNotificationHandler |
backchannel_request.answered |
Hands the call to the notification endpoint of the client to the same lane |
RecordTheServiceProviderOfTheSessionHandler |
saml.service_provider.signed_in |
Records the part of the service provider in the session, for the logout the identity provider starts |
A logout token its client refuses is given up at once. One that cannot be delivered is tried again on the
schedule of beffroi.back_channel_logout, and goes to the failure transport once that schedule runs out. Either
way, logout_token.given_up is raised.
The events of the core¶
| Act | Class | Raised when | Carries beyond the four | Concerns |
|---|---|---|---|---|
user.authenticated |
UserAuthenticated |
A session is opened | The session | A person |
authentication.failed |
AuthenticationFailed |
A login is refused | The method claimed, the reason, the identifier as typed, the user when one answers | A person, or nobody |
session.ended |
AuthenticationSessionEnded |
A session ends | The session, which says who ended it | A person |
password.used |
PasswordUsed |
A password is accepted | The credential | A person |
passkey.used |
PasskeyUsed |
A passkey assertion is verified | The credential, and whether its sign count did not grow | A person |
totp.used |
TotpUsed |
A code of the authenticator application is accepted | The credential | A person |
backup_code.used |
BackupCodeUsed |
A backup code is spent | The credential | A person |
person.provisioned |
PersonProvisioned |
An upstream provider creates a person | The subject and the name of the provider | A person |
credential.enrolled |
CredentialEnrolled |
A passkey or a TOTP is enrolled | The credential | A person |
credential.renamed |
CredentialRenamed |
A passkey is renamed | The credential, and the name it had | A person |
credential.revoked |
CredentialRevoked |
A credential is revoked | The credential | A person |
credential.forgotten |
CredentialForgotten |
The floor of the tenant forgets a credential | The credential | A person |
password.changed |
PasswordChanged |
A person replaces their own password | The credential | A person |
password.rehashed |
PasswordRehashed |
A password is stored again under the algorithm and the parameters of the hasher | The credential | A person |
backup_codes.renewed |
BackupCodesRenewed |
A person draws another set of backup codes | The credential | A person |
enrolment_ticket.issued |
EnrolmentTicketIssued |
An enrolment ticket is issued | The ticket | A person |
person.described_themselves |
PersonDescribedThemselves |
A person says something of themselves | The user, the name of the claim, whether it was taken back | A person |
archive.asked |
PersonAskedForTheirArchive |
A person asks for their archive | The identifier of the archive and the subject | A person |
archive.taken |
PersonTookTheirArchive |
A person downloads it | The same | A person |
archive.dropped |
PersonDroppedTheirArchive |
A person has it dropped | The same | A person |
tenant.key_staged |
TenantKeyStaged |
A key is drawn and staged, published in the JWK Set when its algorithm is one the set carries | The key | Nobody |
tenant.key_rotated |
TenantKeyRotated |
A key takes over | The key activated, and the one it replaced | Nobody |
tenant.key_retired |
TenantKeyRetired |
A key leaves the service | The key, and why | Nobody |
tenant.data_keys_rewrapped |
TenantDataKeysRewrapped |
The data keys are wrapped by another client | The counts, the clients and the master key | Nobody |
tenant.data_keys_destroyed |
TenantDataKeysDestroyed |
The data keys are erased | The counts | Nobody |
kms.unavailable |
KmsUnavailable |
The key management service fails on the path of a request | What the service said | Nobody |
load.budget_spent |
LoadBudgetSpent |
A request is refused for a spent load budget | The budget, the route, the client when one was authenticated | Nobody |
The events of the protocol¶
| Act | Class | Raised when | Carries beyond the four | Concerns |
|---|---|---|---|---|
authorization_code.issued |
AuthorizationCodeIssued |
An authorization code leaves for a client | The client, the subject, the grant, the session, the hash of the code, the scopes, when it expires | A person, a client |
authorization_code.replayed |
AuthorizationCodeReplayed |
A code is presented a second time (RFC 6749 §4.1.2), and the tokens issued from it are revoked | The hash of the code, the subject, the client it was issued to, the client that presented it, the hashes of the tokens revoked | A person |
tokens.issued |
TokensIssued |
The token endpoint answers a client, whatever the grant | The grant type and the grant, the client, the subject, null for a client acting for itself (RFC 6749 §4.4), the session, the scopes granted, when the access token expires, whether an ID token left, what the tokens are bound to, the hash of the refresh token, the claims asked for by name, the authorization details, the shape of the access token; never a token | A person or nobody, a client |
refresh_token.rotated |
RefreshTokenRotated |
A refresh token is replaced by its successor (RFC 6749 §6) | The client, the subject, the family, the hashes of both tokens, when the successor expires | A person, a client |
refresh_token.reuse_detected |
RefreshTokenReuseDetected |
A rotated refresh token is presented again, and its whole family revoked (RFC 9700 §4.13.2) | The client, the subject, the family, the hash presented, the hashes revoked | A person, a client |
refresh_token.outlived_its_session |
RefreshTokenOutlivedItsSession |
A refresh token still recorded active is presented for a session that ended, and refused | The client, the subject, the session, when the session ended | A person, a client |
token.revoked |
TokenRevoked |
A client gives a token back at the revocation endpoint (RFC 7009 §2.1) | The client that revoked it, the subject, null for a token naming nobody, the type of the token, its hash, the grant, the access tokens revoked with a refresh token | A person, or nobody |
client_authentication.failed |
ClientAuthenticationFailed |
A client fails to authenticate, at any endpoint that authenticates one | The reason, the client identifier as typed, the method presented | Nobody |
consent.granted |
ConsentGranted |
A person agrees, on the consent page, on the page that confirms a device code, or when answering a backchannel request | The consent, the client, the subject, the scopes, the authorization details, the consent it takes over from | A person, a client |
consent.revoked |
ConsentRevoked |
A person takes a consent back, on their account or by the console | The consent, the client, the subject, the grants revoked with it, and the hashes of the refresh and access tokens revoked | A person, a client |
introspection.answered |
IntrospectionAnswered |
A resource server is told who an active token names (RFC 7662 §2.2). An inactive token, or one naming nobody, raises nothing | The client that asked, the subject | A person, a client |
userinfo.answered |
UserInfoAnswered |
A client is answered the claims of a person (OIDC Core §5.3) | The client, the subject; never which claims | A person, a client |
logout_token.issued |
LogoutTokenIssued |
A logout token is minted for a client of a session that ended (Back-Channel Logout 1.0 §2.6) | The client, the subject, the logout URI, the session, the token | A person, a client |
logout_token.given_up |
LogoutTokenGivenUp |
A logout token is refused by its client, or could not be delivered within the retries (Back-Channel Logout 1.0 §2.8) | The client, the subject, the session, what the last attempt came back with; never the token | A person, a client |
backchannel_request.answered |
BackchannelRequestAnswered |
A backchannel request is answered, yes or no, for a client registered to be called (CIBA Core §10.2) | The client, the subject, null when the request named nobody, the notification endpoint, the identifier of the request, the notification token | A person or nobody, a client |
pushed_request.refused |
PushedRequestRefused |
A request_uri presented at the authorization endpoint grants nothing (RFC 9126 §4) |
The reason, the client that presented it, the client it was pushed by; never the reference | Nobody |
registered_client.deleted |
RegisteredClientDeleted |
A client that registered itself is gone: deleted at its configuration endpoint (RFC 7592 §2.3), by beffroi:client:delete, or dropped for want of use |
The client | A client |
The reason of client_authentication.failed is one of malformed_credentials, multiple_mechanisms,
mismatched_client_id, unsupported_scheme, no_credentials, unknown_client, unexpected_method,
invalid_secret, invalid_assertion, replayed_assertion, unreadable_client_keys, unexpected_certificate and
unreadable_certificate; the client is answered invalid_request or invalid_client (RFC 6749 §5.2) whatever it
is. The reason of pushed_request.refused is one of unknown_request_uri, expired_request_uri,
already_used_request_uri and other_client; the client is answered invalid_request_uri whatever it is.
The event of the SAML package¶
| Act | Class | Raised when | Carries beyond the four | Concerns |
|---|---|---|---|---|
saml.service_provider.signed_in |
ServiceProviderSignedIn |
Single sign-on answers a service provider with an assertion written from a session | The session, the entity ID of the service provider, the subject; never the NameID it was given |
A person |