Sessions and tokens
Clients and sessions
Section titled “Clients and sessions”The browser is a client of the instance; when the user signs in, the client gets a session. The session lives until the user signs out, it is revoked, or it reaches its lifetime (7 days by default; set in Auth settings, with an optional inactivity timeout). A new sign-in on the same browser replaces the session.
- Production: the client credential is the
__Host-zautha_clientcookie (HttpOnly,Secure,SameSite=Lax) on your Frontend API host, and__Secure-zautha_client_uat(the time of the last sign-in,0when signed out) is written on your domain so your servers can see whether a session may exist. - Development: Zautha’s host sets no cookies. The SDK keeps the client credential and sends it in a header.
A session can be pending: signed in, but with a task left (setting up two-step verification when the instance requires it, or choosing an organization when it requires one). A pending session gets no session token, so your app treats it as signed out until the task is done.
The session token
Section titled “The session token”Your server never calls Zautha to ask who is signed in. The SDK in the browser gets a session token for the
session, a JWT that lives 60 seconds and is renewed before it expires, and sends it to your server: in the
Authorization: Bearer header from your frontend, or as the __Host-zautha_session cookie the SDK writes on your app’s host.
Your server verifies it with the instance’s public keys (its JWKS, at
https://{frontend host}/.well-known/jwks.json), and can do so without any network call when you give the SDK the
public key.
| Claim | Meaning |
|---|---|
iss |
The instance’s Frontend API, https://{frontend host} |
sub |
The user’s id, user_… |
sid |
The session’s id, sess_… |
azp |
The origin the token was issued for (the page that asked for it). Your server accepts a token only for its own origins: the SDKs’ authorizedParties |
iat, nbf, exp |
Issued, valid from, expires: 60 seconds apart |
v |
The token format’s version, 1 |
fva |
Minutes since the session last verified its first and its second factor; the second is -1 for a user without two-step verification. Reverification checks read it |
o |
The active organization, when there is one: id, slg (its slug, when slugs are on), rol (the role key without org:) and per (the role’s custom permissions without org:) |
act |
Only in an impersonation session: { sub, iss? }, who is acting as the user |
Because the token lives a minute, a change (a revoked session, a new role) reaches your server within about a minute
at most. Verify every request; never trust a token’s claims without verifying its signature, issuer, expiry and azp.
The handshake
Section titled “The handshake”A server-rendered page reads the __Host-zautha_session cookie. When it has expired (the tab was closed for a while), the SDK on
your server cannot renew it itself, because the session lives on Zautha’s host:
- Production: the page load is redirected briefly through your Frontend API, which reads
__Host-zautha_clientand sends the browser back with a fresh token. The round trip is bound to the browser that started it by a short-livedHttpOnlystate cookie; a link without the matching state is rendered signed out and never redirects again. - Development: the SDK renews it server to server, with no redirect.
@zautha/nextjs and @zautha/backend’s authenticateRequest do this for you; see
@zautha/backend.
Signing out and revoking
Section titled “Signing out and revoking”zautha.signOut() signs out even when Zautha cannot be reached (offline, an outage): the browser drops the session
token, the session cookie and the session, tells your server through client_uat=0 and the other tabs, and rejects
with the error instead of going to redirectUrl, so you can tell the user. The next page load sends the sign-out again
before anything else, and until it gets through the SDK does not take that session back.
Signing out ends the session; ending it in the dashboard, with the Backend API (POST /v1/backend/sessions/{sessionId}/revoke)
or from another of the user’s devices revokes it (ending another device’s session needs a factor verified in the last
10 minutes, like other sensitive account changes). Sign out every user on the dashboard’s Users page, or
POST /v1/backend/sessions/revoke_all (sessions.revokeAll() in @zautha/backend, Sessions.RevokeAllAsync() in
Zautha.AspNetCore), revokes every session of the instance at once; their session.revoked webhooks follow within
seconds. Completing a reverification
also gives the browser a new client credential. A banned user’s sessions are revoked, and so are a user’s other
sessions when they reset a forgotten password or turn on two-step verification, and when they change their password or another way to sign in (an
authenticator app, backup codes, a passkey, an email address, a connected account) and keep “Sign out of all other
devices” checked (signOutOfOtherSessions in @zautha/js). Tokens already issued stay valid until
they expire, at most 60 seconds. Each change sends a session.* webhook.