Sessions, Consent & Logout
Docs version v1 (main)Nauthera is pre-1.0, in a design-partner programUpdated
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. | 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. | 24h | Ends every session this long after sign-in, used or not. Must be at least idle_ttl. |
auth. | 2160h | How long an ended session is kept in the session history, with where and how the user signed in and why it ended. At most 8760h. |
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 | _ |
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. A sweep that runs every hour on each replica
deletes the sessions that are past either one, and records it as
system.session.reaped. - The user signs out on the hosted UI, or an application sends them through RP-initiated logout (below).
- An administrator ends the session, or every session of the user, through the admin API or the admin dashboard. See Admin API & Users.
- The password is reset, by the user through an emailed link or by an administrator, or the user is deleted. See Admin API & Users.
- An administrator suspends or blocks the account. Every session ends, and stays ended when the account is reactivated or a suspension lifts by itself at its set time. See Admin API & Users.
- The user changes a second factor in one of the ways Multi-Factor Authentication lists. Every session of the user ends except the one the change was made in.
An ended session is kept in the user's session history for
auth., with why it ended: signed out, ended by an
administrator, password reset, account suspended or blocked, second factor changed,
idle timeout or lifetime reached. Administrators read it through the admin API and the
dashboard.
Ending a session also stops the refresh tokens issued in it: the next refresh answers
invalid_grant. A password reset, an administrator ending every session of the user or
suspending or blocking the account, and a change of second factor go further: they
retire every refresh token the account was given until then, from any session, client or
flow, so device-code grants and grants that outlived their session stop too. A grant
from a later sign-in keeps working. See
Tokens & Sender Constraint.
A session that simply expires retires nothing. 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).
New-sign-in alerts
When a user signs in from a browser or a network they have not signed in from before, the server emails them an alert, in their browser's language where it has one (English, German or French), with when it happened, the browser and system, the IP address and, with a geolocation database, the city and country. The alert links to the sign-in app's password reset, for a user who did not sign in. It is checked when a sign-in completes with the password, a passkey alone, an external identity provider or a second factor, and when a registration signs the new account in.
- A browser is recognized by a random marker in a long-lived
HttpOnlycookie,nauthera_browser, scoped and prefixed per organization like the session cookie. - A network is the address's
/24(IPv4) or/48(IPv6), so a new address in the same range is not new. - Each is stored only as a digest of the user's id and the value. A user keeps up to 20 browsers and 20 networks, and one not seen for 180 days is forgotten.
- A user with nothing remembered, such as on their first sign-in, gets no alert. One browser gets at most one alert a day.
- The alert needs the organization to have an email relay. Without one, nothing is sent.
Set geoip.database to a GeoLite2-City or GeoIP2-City file to name the place. The server
re-reads the file when it changes; replace it by renaming the new file into place, as
geoipupdate does. In the Helm chart, set geoip.volume to the volume that holds it.
Administrators see a user's known browsers and networks in the admin API and the dashboard; see Admin API & Users.
Consent
Whether a user is asked to approve a client depends on how the client was registered:
| Client | Asked? |
|---|---|
Registered through the gRPC AdminService (for example with nauthera-cli) | 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 (a loopback redirect) | 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, client_id, 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, an expired or invalid 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 client_id is sent with a valid id_token_hint, the client must be in
the token's audience, or the request is refused. An expired id_token_hint counts as no
hint, so a signed-in user is asked to confirm. Accepting it, as the spec allows, is
planned (#312). ID tokens last 10 minutes by default, so by the time a user signs out,
the ID token from sign-in has often expired.
post_logout_redirect_uri must exactly match a URI registered for the client named by
client_id, or, if no client_id is sent, by the audience of a valid id_token_hint.
After the confirmation screen only client_id counts. If no URI matches, the browser
goes to the server's own /logout page instead of the application. Send client_id on
every logout. Without it, post_logout_redirect_uri is honoured only when a valid
id_token_hint is sent and the user is not asked to confirm.
Telling the applications
When a session ends through logout, the applications the user signed in to during that session are told through the logout URIs they registered:
- Front-channel. Only when
/end_sessionends the session at once (theid_token_hintverifies and names the user signed in here), clients that registered afrontchannel_logout_uriget it loaded in a hidden iframe on a page the server renders before the final redirect. A logout the user confirms on the/logoutscreen and a sign-out on the hosted UI load no front-channel URIs. Even where it runs, front-channel is best effort: browsers that block third-party cookies can keep the application from seeing its own session. - Back-channel. On every logout path, clients that registered a
backchannel_logout_urireceive a signed logout token in a directPOSTfrom the server. Delivery to each client is tried up to three times, five seconds per attempt, with backoff from one second. All deliveries for one logout stop after 30 seconds. Back-channel URIs can be registered only through dynamic registration.
Sessions an administrator ends, by ending them, setting a password, suspending or blocking the account or deleting a user, are announced over back-channel logout too.
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.).
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.