@haneinfo/frontend-auth
v1.1.0
Published
Shared server-side NextAuth integration for Haneinfo Apps
Readme
@haneinfo/frontend-auth
Server-only NextAuth integration shared by Haneinfo Apps. Import from
@haneinfo/frontend-auth/server. Each App passes its login page and optional Keycloak
authorization scope to createFrontendAuth().
The returned session callback exposes the stable refresh error code but never bearer or refresh
tokens. Server actions use getAccessToken() and getKeycloakEndSessionUrl() as needed.
Session readers follow AUTH_URL / NEXTAUTH_URL or the forwarded protocol, including Auth.js's
HTTPS default. Pass useSecureCookies and cookies.sessionToken to the factory when overriding
Auth.js cookie settings; both Auth.js and the token readers use those options.
Refresh-token rotation
The default cookie-backed mode keeps the existing behavior and supports providers that reuse their
refresh token. To use a provider that rotates refresh tokens, pass a FrontendAuthTokenStore to
createFrontendAuth(). The Auth.js JWT cookie then contains an opaque session id; access, refresh,
and ID tokens stay in the server-side store. Both getAccessToken() and the Auth.js JWT callback
refresh under the same store lock, so read-only Server Component renders can safely persist a new
refresh token without writing a cookie.
The application supplies the storage adapter because it owns the database or distributed cache.
Every application instance must use the same store. Its runExclusive(sessionId, operation) must
serialize the full read, provider call, and durable update for a session across processes. update()
must be durable before it returns; create() must not overwrite an existing id. Store token values
encrypted at rest, expire records at sessionExpiresAt, and make delete() idempotent. Auth.js uses
a 30-day JWT session lifetime in this factory, renewed by its JWT callback. Sign-out deletes the
stored record while holding the same lock.
When enabling the store for existing users, an Auth.js JWT callback migrates a legacy cookie into
the store and replaces its tokens with the opaque id. Until that callback has migrated an old
cookie, getAccessToken() returns a still-valid access token; it will not rotate an expired legacy
cookie because a read-only token reader cannot persist the required session id.
The store lock prevents concurrent refreshes from multiple app instances. A process can still fail after the identity provider consumes a refresh token and before the rotated value is durably stored; the local store and remote identity provider cannot commit atomically. Deployments should use an identity-provider grace window when available and monitor refresh failures. The adapter must never log token values.
