From ba29dac805570bf0a5f72f86987062e348045da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Thu, 18 Jul 2024 14:24:51 +0200 Subject: [PATCH] Implement solution provided by @voegtlel and @andpii https://github.com/manfredsteyer/angular-oauth2-oidc/issues/850#issuecomment-889921776 https://github.com/manfredsteyer/angular-oauth2-oidc/issues/850#issuecomment-1557286966 --- projects/lib/src/oauth-service.ts | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/projects/lib/src/oauth-service.ts b/projects/lib/src/oauth-service.ts index aa699704..060f2d6c 100644 --- a/projects/lib/src/oauth-service.ts +++ b/projects/lib/src/oauth-service.ts @@ -107,6 +107,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { protected sessionCheckTimer: any; protected silentRefreshSubject: string; protected inImplicitFlow = false; + protected lastUpdatedAccessToken: string | null = null; protected saveNoncesInLocalStorage = false; private document: Document; @@ -171,6 +172,10 @@ export class OAuthService extends AuthConfig implements OnDestroy { } this.setupRefreshTimer(); + + if (this.hasValidAccessToken()) { + this.lastUpdatedAccessToken = this.getAccessToken(); + } } private checkLocalStorageAccessable() { @@ -927,6 +932,27 @@ export class OAuthService extends AuthConfig implements OnDestroy { * method silentRefresh. */ public refreshToken(): Promise { + // Handle multiple browser tabs if navigator.locks is available + if (!navigator.locks) { + return this._refreshToken(); + } + return navigator.locks.request( + `refresh_token_${location.origin}`, + async (): Promise => { + if (this.lastUpdatedAccessToken !== this.getAccessToken()) { + // Was already updated in another tab/window + this.eventsSubject.next(new OAuthSuccessEvent('token_received')); + this.eventsSubject.next(new OAuthSuccessEvent('token_refreshed')); + this.lastUpdatedAccessToken = this.getAccessToken(); + return; + } else { + // Simply run the original update + return this._refreshToken(); + } + } + ); + } + protected _refreshToken(): Promise { this.assertUrlNotNullAndCorrectProtocol( this.tokenEndpoint, 'tokenEndpoint' @@ -1051,6 +1077,32 @@ export class OAuthService extends AuthConfig implements OnDestroy { public silentRefresh( params: object = {}, noPrompt = true + ): Promise { + // Handle multiple browser tabs if navigator.locks is available + if (!navigator.locks) { + return this._silentRefresh(params, noPrompt); + } + return navigator.locks.request( + `silent_refresh_${location.origin}`, + async (): Promise => { + if (this.lastUpdatedAccessToken !== this.getAccessToken()) { + // Was already updated in another tab/window + this.eventsSubject.next(new OAuthSuccessEvent('token_received')); + this.eventsSubject.next(new OAuthSuccessEvent('token_refreshed')); + const event = new OAuthSuccessEvent('silently_refreshed'); + this.eventsSubject.next(event); + this.lastUpdatedAccessToken = this.getAccessToken(); + return event; + } else { + // Simply run the original update + return this._silentRefresh(params, noPrompt); + } + } + ); + } + protected _silentRefresh( + params: object = {}, + noPrompt = true ): Promise { const claims: object = this.getIdentityClaims() || {}; @@ -1677,6 +1729,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { customParameters?: Map ): void { this._storage.setItem('access_token', accessToken); + this.lastUpdatedAccessToken = accessToken; if (grantedScopes && !Array.isArray(grantedScopes)) { this._storage.setItem( 'granted_scopes', @@ -2496,6 +2549,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { const id_token = this.getIdToken(); this._storage.removeItem('access_token'); + this.lastUpdatedAccessToken = null; this._storage.removeItem('id_token'); this._storage.removeItem('refresh_token');