Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,12 @@ session cookie that admits it exactly as a credential would. That cookie is
host, and `Secure` whenever the instance URL is `https`. It carries its own
expiry and the signature that proves this instance minted it, so no session is
kept in memory and every replica of an instance accepts the sessions the others
mint. A browser holds one session per instance, whichever policy established
it.
mint.

A signed-in browser also holds a short-lived transaction cookie during a login,
and a renewal marker naming the policy it signed in under, which is what lets an
expired session be renewed against the provider without asking the person again.
The marker carries no credential.

### Login

Expand All @@ -186,6 +190,11 @@ discovered from the policy's issuer, and sets a short-lived transaction cookie
that binds the login to this browser. That cookie is what the callback checks
before it acts on anything the provider says.

`to` names where to land once the login completes, and is honoured only when it
is a path on this instance, so the endpoint cannot be turned into an open
redirect by whoever composes the link. Anything else falls back to the referring
page, and then to the first path the policy declares.

=== "303"

The provider's authorization URL in `Location`, with the transaction
Expand Down Expand Up @@ -267,12 +276,13 @@ behalf.
POST /self/v1/auth/logout
```

Both cookies are expired before anything else is decided, and whether or not
the request carried them, so no outcome leaves a session behind. Where the
session names a policy whose provider offers to end its own session, the
browser is then sent on to do so, carrying the identity token as proof of
whose session it is asking to end. That is what stops the next sign-in from
silently admitting whoever used the browser last.
Every cookie this endpoint mints is expired before anything else is decided,
and whether or not the request carried it, so no outcome leaves a session
behind or a browser eligible to renew one. Where the session names a policy
whose provider offers to end its own session, the browser is then sent on to do
so, carrying the identity token as proof of whose session it is asking to end.
That is what stops the next sign-in from silently admitting whoever used the
browser last.

This endpoint answers `POST` rather than `GET` because signing out changes
state at the provider, which [RFC 9110
Expand All @@ -281,15 +291,10 @@ outside what a safe method may do. A link to it would be followed by anything
that prefetches or unfurls URLs, signing the person out unasked, so the
control that reaches it is a form submission.

A session is a sealed value this instance keeps no record of, which is what
lets it stay stateless. Signing out therefore takes the session from the
browser rather than revoking it, and a copy taken beforehand stays usable
until it expires. Sessions are short-lived for that reason.

=== "303"

Both cookies expired in `Set-Cookie`, and either the provider's end-session
URL or the instance root in `Location`.
The session, transaction and renewal cookies expired in `Set-Cookie`, and
either the provider's end-session URL or the instance root in `Location`.

=== "405"

Expand Down
61 changes: 38 additions & 23 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,17 @@ never what to expose. When a path is governed by more than one policy, the
policies are unioned, so a single collection can admit both a machine presenting a
credential and a user carrying a session.

None of it is stored. Every credential is checked as it arrives, against the
environment or against the issuer's published key set, and a session is a sealed
value the instance keeps no record of, so any replica verifies any of them on
its own, with no session or state to look up and no coordination with the
others. Public key material is still fetched from an issuer and cached, which
is a lookup of what an issuer publishes rather than of who is signed in. That
is what lets an instance scale horizontally, and it is why taking access away
works differently for each policy type, described under each below. Sessions
belong to `oidc` alone: a `jwt` policy has no session and no cookie, whether or
not it names the same provider.

**The UNIX model**: Visibility and access are kept separate, following the UNIX
filesystem model. A policy that governs a directory does not erase it from its
parent's listing. Just as `ls` reveals a directory you cannot `cd` into, a
Expand Down Expand Up @@ -371,7 +382,9 @@ regardless of type:
### API Key

Consumers present a key through the `Authorization` header using the `Bearer`
scheme ([RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)).
scheme ([RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)). Revoking one
means dropping its variable from `keys` and restarting, which leaves every other
key in the policy working.

An `apiKey` policy declares the following additional properties:

Expand Down Expand Up @@ -467,7 +480,12 @@ A token is admitted only when its signature verifies against the issuer's key
set, its `iss` claim matches the policy's `issuer`, its `aud` claim includes the
policy's `audience`, its signature algorithm is one the policy allows, and it is
within its validity period. A token that fails any of these is denied, with the
same response as any other unauthenticated request.
same response as any other unauthenticated request. Nothing here asks the
issuer whether a subject is still welcome, and no token is ever recorded as
withdrawn, so a valid token is accepted until it expires. The issuer can cut
that short by retiring the key its tokens were signed with, which reaches every
token signed under it once the cached key set refreshes. Short token lifetimes
are what bound exposure otherwise.

| Property | Type | Required | Default | Description |
|-----------------|------|----------|---------|-------------|
Expand Down Expand Up @@ -557,19 +575,24 @@ follows is signed with a secret of the instance's own, unrelated to the provider
policy declares. The order of `paths` therefore decides where signing in
leaves somebody, though it never changes what the policy gates.

!!! note

A browser holds one session per instance, whichever interactive policy
established it, so signing in with a second one ends the first. Separate
browser profiles are the way to hold both at once.

Signing out ends the provider's session too, where the provider offers to
end it, so signing in again asks who you are rather than admitting whoever
used the browser last. It takes the session from the browser rather than
revoking it, since a session is a sealed value this instance keeps no
record of, which is what lets it stay stateless and scale horizontally. A
copy taken beforehand therefore stays usable until it expires, which is why
sessions are short-lived.
A browser holds one session per instance, whichever interactive policy
established it, so signing in with a second one ends the first. A session lasts
an hour and renews without anybody noticing, by sending the browser back to the
provider, which answers without displaying anything where the sign-in still
stands. Only a navigation renews: a script calling the API with an expired
session is denied plainly rather than redirected, since a redirect chain to an
identity provider is not something it can follow.

Because a session is a sealed value rather than a record, nothing can be struck
out. Signing out takes the session from the browser, so a copy taken beforehand
stays usable until it expires. Ending every session at once means leaving a
single new secret in `sessionSecrets`, dropping the ones sessions were signed
under, and restarting. That is the only immediate lever there is, and note that
merely adding a secret does the opposite: existing sessions keep working, which
is what makes rotation invisible. There is deliberately no OpenID Connect
Back-Channel Logout, since acting on one means keeping a record of which
sessions are dead and consulting it on every request, which is the round trip
statelessness avoids.

!!! warning

Expand Down Expand Up @@ -622,14 +645,6 @@ sign in through their identity provider to reach it:
admits both a machine that presents a credential and a user who signs in, so
one endpoint can serve continuous integration and users at once.

!!! note

The session signing secret is the instance's own, unrelated to the provider.
To rotate it without invalidating live sessions, the environment variable may
hold several secrets, one per line, newest first. New session cookies are
signed with the first, while existing cookies are accepted against any, so an
old secret can be retired once the sessions signed with it have expired.

## Extends

The `extends` property enables configuration inheritance, allowing you to build
Expand Down
Loading