Sessions, Consent & Logout
Sessions
A successful sign-in creates a login session on the server and a cookie in the
browser. Single sign-on comes from that cookie: while it is valid, /authorize signs the
user in to the next application without asking for a password.
| Key | Default | Meaning |
|---|---|---|
auth.session.cookie_name | nauthera_session | Base name of the session cookie |
auth.session.cookie_domain | empty | Leave empty unless the cookie must reach subdomains |
auth.session.secure | auto | auto sets Secure when the issuer is https, which it always is. always and never force it. |
auth.session.idle_ttl | 12h | Ends a session nobody used for this long. Each use extends it. |
auth.session.absolute_ttl | 24h | Ends every session this long after sign-in, used or not. Must be at least idle_ttl. |
auth.pending.cookie_name | nauthera_pending | Short-lived cookie between the password and the MFA step. Must differ from the session name. |
auth.pending.ttl | 10m | How long that step may take |
Cookie names
Sessions belong to one organization, so each organization has its own cookie. The main
organization uses the base name; any other organization appends its slug, so acme
uses nauthera_session_acme. A sign-in at one organization never signs the user in to
another.
The prefix follows the cookie's attributes:
| Attributes | Cookie name |
|---|---|
Secure, no domain (the default) | __Host-nauthera_session |
Secure, with cookie_domain | __Secure-nauthera_session |
Not Secure (secure: never) | nauthera_session |
The session cookie is HttpOnly and SameSite=Lax.
What ends a session
- The idle or absolute lifetime runs out. Sessions past their absolute lifetime are
deleted by a sweep that runs every hour on each replica and is recorded as
system.session.reaped. - The user signs out on the hosted UI, or an application sends them through RP-initiated logout (below).
- An administrator sets the user's password or deletes the user. See Admin API & Users.
Ending a session also stops the refresh tokens issued in it: the next refresh answers
invalid_grant. A session that simply expires does not do this. Its refresh tokens keep
working until their own lifetime runs out; an absolute lifetime for a refresh-token
family is tracked in #310.
Signing in again does not yet replace an earlier session in the same browser (#311).
Consent
Whether a user is asked to approve a client depends on how the client was registered:
| Client | Asked? |
|---|---|
Registered through the gRPC AdminService (nauthera-cli, the operator) | No. An administrator registered it for a fixed set of scopes, which counts as administrative consent (OIDC Core §3.1.2.4). |
| Registered through dynamic registration | Yes, the first time, and again whenever it asks for a scope the user has not approved. |
A public client whose redirect URI is not https (custom scheme or loopback) | Yes, every time, whoever registered it. Another app on the device could claim the same redirect (RFC 8252 §8.6). |
Any client sending prompt=consent | Yes |
An approval is stored per organization, user, client and resource indicator. It covers any later request for the same or fewer scopes, and approving more scopes widens it.
Two things are missing today:
- Approvals never expire.
- They cannot be withdrawn. There is no API or screen to revoke an approval.
Both are tracked in #321.
Logout
RP-initiated logout
An application signs the user out by sending the browser to /end_session (GET or form
POST) with id_token_hint, post_logout_redirect_uri and state. The parameters are
listed in Connect an Application.
What happens next depends on who is signed in:
| Situation | Result |
|---|---|
id_token_hint verifies and names the user signed in here | The session ends at once and the browser goes to post_logout_redirect_uri. |
| Nobody is signed in | Nothing to end. The browser goes straight to post_logout_redirect_uri. |
| No hint, or a hint for someone else | The hosted /logout screen asks the user to confirm. The session ends only after they do. |
The confirmation also stops a cross-site page from signing a user out with a plain link
or image. When both client_id and id_token_hint are sent, the client must be in the
token's audience, or the request is refused. post_logout_redirect_uri must match a
registered URI exactly; otherwise the user lands on a logged-out page. An expired
id_token_hint is refused today, though the spec allows it (#312).
Telling the other applications
When a session ends through logout, every application the user signed in to during that session is told:
- Front-channel. Clients that registered a
frontchannel_logout_uriget it loaded in a hidden iframe on a page the server renders before the final redirect. This is best effort: browsers that block third-party cookies can keep the application from seeing its own session. - Back-channel. Clients that registered a
backchannel_logout_urireceive a signed logout token in a directPOSTfrom the server. Each delivery is tried up to three times, five seconds per attempt, with backoff from one second, and gives up after 30 seconds in total. Back-channel URIs can be registered only through dynamic registration.
Sessions that an administrator ends, by setting a password or deleting a user, are not announced to applications yet (#519).
Session management
OpenID Connect Session Management lets an application poll an iframe to notice that the
user signed out elsewhere. It is off by default (auth.session_management.enabled).
When it is on, the server sets an extra browser-state cookie, nauthera_op_bs, which is
readable by script and SameSite=None, and returns session_state on the authorization
response.
The advertised check_session_iframe URL answers only with a client_id query
parameter appended (#313), and how the frame learns which origins may talk to it is
still open (#367). Prefer back-channel logout.