AUTH4D-65 browser SDK in-memory refresh and silent renewal
@auth4/sdk-browser can use the rotating browser refresh tokens added by AUTH4D-63. This is an SDK
change only; it adds no endpoints and uses the existing POST /oauth/token and
POST /oauth/revoke routes.
Opt-in
new Auth4BrowserClient({ issuer, clientId, redirectUri, refresh: true }) enables refresh. The
browser application must also have allowBrowserRefresh enabled, or the authorization request
fails with invalid_scope.
- With
refresh: true,login()andauthorizationUrl()addoffline_accessto the requested scopes (the default becomesopenid profile email offline_access). - Without it, behavior is unchanged:
offline_accessinscopethrowsconfiguration_error, and anyrefresh_tokenin a token response is discarded.
Renewal
- The refresh token is held in an ES private field in memory only. It is never written to
sessionStorage,localStorage, cookies or the DOM, and is not part of any event orJSON.stringify(client)output. A page reload or new tab therefore still needs a new sign-in. - Renewal starts before the access token expires: 60 seconds early plus up to 30 seconds of random
jitter (each bounded to a quarter of the token lifetime), so a five-minute token renews 210 to 240
seconds after it was issued. The request is
grant_type=refresh_token&refresh_token=…&client_id=…to the discovered token endpoint, and the rotated refresh token replaces the old one. - A renewed
id_token, when returned, is verified like the sign-in ID token. Itsnoncemay be absent, and itssubmust match the signed-in user. - Renewal is single-flight. The scheduled timer and every concurrent
getAccessTokenSilently()call share one token request. - Network errors, HTTP 5xx and HTTP 429 are transient. The current token stays usable until it expires, and renewal is retried after at most 15 seconds.
- Any other outcome ends the session:
invalid_grant(reuse, revocation or idle/absolute expiry),unauthorized_client(the application opted out), or a token response that cannot be validated. The SDK clears memory, emitssigned_outwith reasonrefresh_rejected, and throwsrefresh_failed.
API additions
| Member | Behavior |
|---|---|
refresh?: boolean option |
Opt in to offline_access and in-memory renewal. |
getAccessTokenSilently() |
Async. Returns a live access token, renewing first if the session is in its renewal window. Without refresh it behaves like getAccessToken(). |
onSessionEvent(listener) |
Subscribes to { type: "renewed", session } and { type: "signed_out", reason: "logout" | "refresh_rejected" }. Returns an unsubscribe function. Events never carry token values. |
refresh_failed error code |
A renewal failed. If the session ended, sign in again. |
getAccessToken() stays synchronous. With refresh enabled and an already-expired token (for
example after a throttled background tab), it throws token_expired, keeps the session, and starts
a renewal. getSession() keeps returning the session while that renewal is pending.
Sign-out
logout() clears memory, emits signed_out with reason logout, and then revokes the refresh
token (token_type_hint=refresh_token), which ends its family and the server session. It then
revokes the access token as before.
Source: docs/api-spec/auth4d-65.md