AUTH4D-11 sessions and refresh behavior
This ticket adds the authoritative session service and its storage adapters; it does not add HTTP
routes. The API composition should call SessionService after a trusted sign-in or device approval,
and from the OAuth token, revocation, and logout route factories.
Session creation issues an RS256 access token for the requested client audience, with only scopes
allowed by the current tenant application policy. Access and ID tokens each expire after five
minutes. An ID token is returned only when the request included openid; its audience is the
client ID, and it preserves the original auth_time during refresh. Browser applications also
receive a tenant-scoped opaque handle in a __Host-auth4_session cookie (Secure, HttpOnly,
SameSite=Lax, Path=/, no Domain). Native applications receive no browser cookie.
Refresh tokens are issued only for offline_access and a client that permits the refresh-token
grant. They are client-bound, rotate on each successful refresh, idle-expire after seven days, and
have a thirty-day absolute lifetime. Reuse of a consumed refresh token atomically revokes its whole
family and associated session, including the current successor. Concurrent refreshes run through
SQLite-backed Durable Object transactions; a duplicate refresh cannot retain a live successor.
Disabled users, disabled clients, and unavailable tenants are rejected before refresh and revoke the
associated family.
Browser handles have a twelve-hour idle deadline and a seven-day absolute deadline. Online access token checks verify the signed access-token class and consult Durable Object session state so revocation takes effect immediately. Offline JWT checks continue to have a maximum five-minute revocation delay.
D1 session_indexes stores only tenant, user, client, session ID, timestamps, and revocation
metadata for console queries. It never stores refresh tokens, browser handles, or their hashes and
is never used to authenticate or refresh a session. If D1 indexing fails during session creation or
browser-session touch, the service fails closed and revokes the authority record where possible.
The API Worker must register and bind SessionStateStore as a tenant-routed SQLite Durable Object
namespace before constructing the production service. Each object name and each stored row includes
the tenant ID; its RPC methods are internal Worker calls and have no public HTTP surface.
Source: docs/api-spec/auth4d-11.md