diff --git a/docs/migration.md b/docs/migration.md index 5f6945d43..731feb3eb 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -34,11 +34,14 @@ removed. - type of `popupWindowFeatures` changed from a string to a dictionary - additionally, its default dimensions are now responsive to the opener window's -- a new property `revokeTokenTypes: ('access_token' | 'refresh_token')[]` was - added +- a new property `revokeTokenTypes: ('access_token' | 'refresh_token')[]` was added - by default, `UserManager` will attempt revoking both token types when `revokeTokensOnSignout` is `true`. Compared to 1.x, sign out will now fail if revocations fail. +- a new property `retrySilentRenew: boolean` was added + - by default `SilentRenewService` will retry every 5s until it receives a response + from the authority server. If you depend on your own custom silent renew error + event handler you can set `false`. ### [UserManager](https://authts.github.io/oidc-client-ts/classes/UserManager.html) diff --git a/docs/oidc-client-ts.api.md b/docs/oidc-client-ts.api.md index 79ce1114d..daca00b7d 100644 --- a/docs/oidc-client-ts.api.md +++ b/docs/oidc-client-ts.api.md @@ -960,6 +960,7 @@ export interface UserManagerSettings extends OidcClientSettings { // (undocumented) query_status_response_type?: string; redirectMethod?: "replace" | "assign"; + retrySilentRenew?: boolean; revokeTokensOnSignout?: boolean; revokeTokenTypes?: ("access_token" | "refresh_token")[]; silent_redirect_uri?: string; @@ -998,6 +999,8 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore { // (undocumented) readonly redirectMethod: "replace" | "assign"; // (undocumented) + readonly retrySilentRenew: boolean; + // (undocumented) readonly revokeTokensOnSignout: boolean; // (undocumented) readonly revokeTokenTypes: ("access_token" | "refresh_token")[]; diff --git a/src/SilentRenewService.ts b/src/SilentRenewService.ts index 2638b0aa4..55f5a7564 100644 --- a/src/SilentRenewService.ts +++ b/src/SilentRenewService.ts @@ -1,9 +1,10 @@ // Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. -import { Logger } from "./utils"; +import { Logger, Timer } from "./utils"; import type { UserManager } from "./UserManager"; import type { AccessTokenCallback } from "./AccessTokenEvents"; +import { ErrorResponse } from "./ErrorResponse"; /** * @internal @@ -11,6 +12,7 @@ import type { AccessTokenCallback } from "./AccessTokenEvents"; export class SilentRenewService { protected _logger = new Logger("SilentRenewService"); private _isStarted = false; + private readonly _retryTimer = new Timer("Retry Silent Renew"); public constructor(private _userManager: UserManager) {} @@ -19,6 +21,7 @@ export class SilentRenewService { if (!this._isStarted) { this._isStarted = true; this._userManager.events.addAccessTokenExpiring(this._tokenExpiring); + this._retryTimer.addHandler(this._tokenExpiring); // this will trigger loading of the user so the expiring events can be initialized try { @@ -34,6 +37,8 @@ export class SilentRenewService { public stop(): void { if (this._isStarted) { + this._retryTimer.cancel(); + this._retryTimer.removeHandler(this._tokenExpiring); this._userManager.events.removeAccessTokenExpiring(this._tokenExpiring); this._isStarted = false; } @@ -44,7 +49,15 @@ export class SilentRenewService { try { await this._userManager.signinSilent(); logger.debug("silent token renewal successful"); - } catch (err) { + } + catch (err) { + if (this._userManager.settings.retrySilentRenew && !(err instanceof ErrorResponse)) { + // no response from authority server, e.g. IFrame timeout, ... + logger.warn("Error from signinSilent:", err, "retry in 5s"); + this._retryTimer.init(5); + return; + } + logger.error("Error from signinSilent:", err); this._userManager.events._raiseSilentRenewError(err as Error); } diff --git a/src/UserManagerSettings.ts b/src/UserManagerSettings.ts index d20d66a8a..bbcf22707 100644 --- a/src/UserManagerSettings.ts +++ b/src/UserManagerSettings.ts @@ -39,6 +39,8 @@ export interface UserManagerSettings extends OidcClientSettings { validateSubOnSilentRenew?: boolean; /** Flag to control if id_token is included as id_token_hint in silent renew calls (default: false) */ includeIdTokenInSilentRenew?: boolean; + /** Retry silent renew every 5s until we receive a response from the authority server (default: true) */ + retrySilentRenew?: boolean; /** Will raise events for when user has performed a signout at the OP (default: false) */ monitorSession?: boolean; @@ -84,6 +86,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore { public readonly automaticSilentRenew: boolean; public readonly validateSubOnSilentRenew: boolean; public readonly includeIdTokenInSilentRenew: boolean; + public readonly retrySilentRenew: boolean; public readonly monitorSession: boolean; public readonly monitorAnonymousSession: boolean; @@ -110,6 +113,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore { automaticSilentRenew = true, validateSubOnSilentRenew = true, includeIdTokenInSilentRenew = false, + retrySilentRenew = true, monitorSession = false, monitorAnonymousSession = false, @@ -137,6 +141,7 @@ export class UserManagerSettingsStore extends OidcClientSettingsStore { this.automaticSilentRenew = automaticSilentRenew; this.validateSubOnSilentRenew = validateSubOnSilentRenew; this.includeIdTokenInSilentRenew = includeIdTokenInSilentRenew; + this.retrySilentRenew = retrySilentRenew; this.monitorSession = monitorSession; this.monitorAnonymousSession = monitorAnonymousSession;