Skip to content

The v1 docs follow nauthera-server's main branch and its 0.x releases on the way to 1.0, so fields and APIs can still change.

v1

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.

KeyDefaultMeaning
auth.session.cookie_namenauthera_sessionBase name of the session cookie
auth.session.cookie_domainemptyLeave empty unless the cookie must reach subdomains
auth.session.secureautoauto sets Secure when the issuer is https, which it always is. always and never force it.
auth.session.idle_ttl12hEnds a session nobody used for this long. Each use extends it.
auth.session.absolute_ttl24hEnds every session this long after sign-in, used or not. Must be at least idle_ttl.
auth.session.history_retention2160hHow 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_namenauthera_pendingShort-lived cookie between the password and the MFA step. Must differ from the session name.
auth.pending.ttl10mHow long that step may take

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:

AttributesCookie 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. 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.session.history_retention, 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 HttpOnly cookie, 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.

Whether a user is asked to approve a client depends on how the client was registered:

ClientAsked?
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 registrationYes, 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=consentYes

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:

SituationResult
id_token_hint verifies and names the user signed in hereThe session ends at once and the browser goes to post_logout_redirect_uri.
Nobody is signed inNothing to end. The browser goes straight to post_logout_redirect_uri.
No hint, an expired or invalid hint, or a hint for someone elseThe 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_session ends the session at once (the id_token_hint verifies and names the user signed in here), clients that registered a frontchannel_logout_uri get it loaded in a hidden iframe on a page the server renders before the final redirect. A logout the user confirms on the /logout screen 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_uri receive a signed logout token in a direct POST from 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.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.