The application and revocation
On this page
A person can be disabled, or a session revoked, while they still hold a valid cookie or token. By default, Jadpo looks up the caller's record on every request, so revoking a session or disabling a user takes effect on the very next request. You don't write anything for that. Sessions and tokens are random values Jadpo looks up in the database.
application { } in app.jadpo can change this with one setting,
revocation_delay:, and also holds the app's display_name:.
Fewer database reads: revocation_delay:
Use it when looking up the caller's record on every request costs too much, and a short window in which a disabled user can still act is acceptable.
application { revocation_delay: 5m}
With revocation_delay: 5m, Jadpo signs what it learned from the caller's
record into their session or token and trusts that for up to five minutes,
without reading the database. A user you disable may keep working for up
to five minutes. After that, the next request reads the record again.
Three kinds of request always read the record, whatever the delay:
- a request with a token from an outside identity provider
(
external_token, see Ways to sign in); - a request that sends a service key itself, rather than a token exchanged for one;
- a request to a route marked
authentication: RouteAuthentication.fresh(Routes: signed in by default).
This is the only revocation setting. Leave it out for the strict behaviour.
How a delayed session is renewed
A credential is issued the same way with or without a delay. A session's cookie and an API token are 32 random bytes, and Jadpo keeps only a verifier of them, a one-way digest that can't be turned back into the credential.
The first request with a new credential reads the record. Its response renews the credential with a part Jadpo signs, holding what that read found and when it stops counting: the delay after the read, or the session's end if that is sooner. Every request after that signs in from the signed part alone. When the part expires, the next request reads the record again and renews it the same way.
- A browser session's renewed cookie comes back in
Set-Cookie, which the browser keeps by itself. - An API token's comes back in an
X-Jadpo-Tokenresponse header. Your client keeps it and sends it from then on. A client that ignores it still signs in, but reads the record on every request. - A token a service gets from
POST /auth/exchange(see Exchanging a service key for a token) is signed when it is given and never renewed. When it expires, the service exchanges its key again.
Signing keys
With a delay, each way of signing in needs a signing_key:, which reads a
SigningKey setting from your configuration
(Signing keys). New parts are
signed with the setting's current value, and parts signed with its
_PREVIOUS value still count, so you can rotate a key without signing
anyone out.
A signed part the key doesn't open, or one that has expired, is never refused for that. The request reads the record, as it would with no delay.
The app's name: display_name:
display_name: is your app's name as people see it, such as "Notes".
Jadpo's own emails name your app by it, so it is required once you switch
on magic links or another
built-in email.
A whole app with a delay
The settings, the delay, a user and a service, and each way of signing in with its key:
config { browser_origin: Url session_signing_key: SigningKey}
use journal/entities { Member, Partner }use journal/types { MemberStatus, PartnerStatus }application { revocation_delay: 5m}principal { user: { subject: Member.handle active: Member.status == MemberStatus.active } service: { subject: Partner.name active: Partner.status == PartnerStatus.active }}
authentication { browser_session: { cookie: "notes_session" signing_key: config.session_signing_key origin: config.browser_origin } service_key: { signing_key: config.session_signing_key }}
A partner signs in with its key, which always reads the record, or with
the token the exchange gave it for that key, which reads nothing until it
expires. A fresh route reads the record either way:
use journal/types { PartnerView }use journal/queries { this_partner }/// What a partner reads, signed in with its key or a token the exchange gave.routes Partners { prefix: /partners /// Signs in without a read while the token's signed part lasts. GET /me { output: PartnerView run: this_partner(current_principal.service) } /// Reads the partner's record now, whatever the token says. GET /me/fresh { authentication: RouteAuthentication.fresh output: PartnerView run: this_partner(current_principal.service) }}
The API record, the file jadpo api update writes to list each API's
routes (The public API record), lists
the exchange Jadpo answers, as jadpo routes and OpenAPI do
(Generated routes):
// Generated by Jadpo from the routes of API main; update it with `jadpo api update`, never by hand.record api 1api main authentication browser_session authentication service_keyPOST /auth/exchange generated exchange caller service output ExchangedToken status 200GET /openapi.json generated openapi status 200GET /partners/me caller service output PartnerView status 200 fails partner_not_foundGET /partners/me/fresh caller service output PartnerView status 200 fails partner_not_foundoutput ExchangedToken expires_at: Instant token: ServiceTokenoutput PartnerView name: Text { min_length: 3, max_length: 40 }failure partner_not_found status 404
Markdown for agents: /docs/the-application-and-revocation.md.Built from Jadpo main at a707efb.