enlace_auth.auth.revocation#
Credential revocation: end everything an account holds when its credentials change.
An account’s access outlives a password change in three places, each with its own
lifetime: browser sessions (session_max_age), OAuth connector
refresh-token families (refresh_family_max_lifetime), and the per-app
shared-password cookies (which are not per account at all – see
shared_password_fingerprint()). This module is the one place that knows how
to end the first two, so every path that changes an account’s credentials
(admin delete, admin password set, self-service change, reset-link redemption,
the set-password CLI) calls a single make_on_credentials_changed() hook
instead of each remembering its own list.
Kept free of FastAPI/Authlib imports so the CLI can use it without the
[oauth] extra.
Functions
|
Return the hook every credential-changing path calls. |
|
How long a family tombstone must live: past every token of the family. |
|
Revoke one refresh-token family; return how many token records went. |
|
Revoke every refresh family issued to email; return how many families. |
|
Store key of the tombstone that marks a whole refresh family revoked. |
|
Store key of the marker that says "nothing email authorized before T". |
|
The time before which every authorization by email is revoked, if any. |
|
True iff a verified shared-cookie value was minted under password_hash. |
|
A short keyed fingerprint of an app's CURRENT shared-password hash. |
Classes
|
|
- class enlace_auth.auth.revocation.CredentialsChanged(*args, **kwargs)[source]#
Bases:
Protocolon_credentials_changed(email, *, keep=None) -> None.keep names one browser session id to spare (the browser that just changed its own password stays signed in).
- enlace_auth.auth.revocation.make_on_credentials_changed(session_store, *, refresh_store=None, code_store=None, tombstone_ttl=0, marker_ttl=0, reason="the account's credentials changed")[source]#
Return the hook every credential-changing path calls.
Always revokes the account’s browser sessions (sparing keep); when a refresh_store is given, also revokes the account’s connector refresh families (and unredeemed codes in code_store). A failure to revoke connector families is logged loudly but does not undo the password change, which has already been written by the caller.
- Return type:
- enlace_auth.auth.revocation.refresh_tombstone_ttl(*, refresh_token_ttl, refresh_reuse_detection)[source]#
How long a family tombstone must live: past every token of the family.
- Return type:
- enlace_auth.auth.revocation.revoke_refresh_family(refresh_store, family, *, reason, tombstone_ttl, now=None)[source]#
Revoke one refresh-token family; return how many token records went.
The tombstone is written FIRST: revocation expressed only as the absence of records loses to a worker concurrently rotating the family (its successor is written after our scan). A positive marker cannot be raced – the refresh grant refuses any record whose family carries one.
- Return type:
- enlace_auth.auth.revocation.revoke_refresh_subject(refresh_store, email, *, reason, tombstone_ttl, code_store=None, marker_ttl=0, now=None)[source]#
Revoke every refresh family issued to email; return how many families.
Matches the subject case-insensitively. First writes a subject marker (see
subject_revoked_before()) that lives marker_ttl seconds – give it the family max lifetime – so authorizations racing this call are refused too. Then tombstones each existing family and drops the subject’s unredeemed authorization codes from code_store when given. Access JWTs already issued are self-contained and live out their (short) TTL.- Return type:
- enlace_auth.auth.revocation.revoked_family_key(family)[source]#
Store key of the tombstone that marks a whole refresh family revoked.
- Return type:
True iff a verified shared-cookie value was minted under password_hash.
- Return type:
A short keyed fingerprint of an app’s CURRENT shared-password hash.
Signed into the
shared_auth_<app>cookie and compared by the middleware, so rotating the shared password invalidates every cookie minted under the old one. Keyed with signing_key so the cookie (whose payload is readable, only signed) reveals nothing about the hash.- Return type:
- enlace_auth.auth.revocation.subject_marker_key(email)[source]#
Store key of the marker that says “nothing email authorized before T”.
- Return type:
- enlace_auth.auth.revocation.subject_revoked_before(refresh_store, email)[source]#
The time before which every authorization by email is revoked, if any.
A scan-and-delete revocation cannot see a family that another worker is creating at that very moment (its code already consumed, its first refresh record not yet written), nor a code issued from a session read just before the change. The marker closes both: the code grant refuses codes issued at or before it, and the refresh grant refuses families authorized at or before it.