mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
AAE-24081 Fix refresh token error with multiple tabs opened (#9964)
* Fix refresh token error with multiple opened tabs (kwnown angular-oauth2-oidc issue => https://github.com/manfredsteyer/angular-oauth2-oidc/issues/850) * fix typo * AAE-24081 test silent refresh and token refresh are called when automatic silent refresh is setup
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { OAuthService, OAuthEvent, OAuthStorage, AUTH_CONFIG } from 'angular-oauth2-oidc';
|
||||
import { OAuthService, OAuthEvent, OAuthStorage, AUTH_CONFIG, TokenResponse } from 'angular-oauth2-oidc';
|
||||
import { Subject } from 'rxjs';
|
||||
import { RedirectAuthService } from './redirect-auth.service';
|
||||
import { AUTH_MODULE_CONFIG } from './auth-config';
|
||||
@@ -31,7 +31,13 @@ describe('RedirectAuthService', () => {
|
||||
const oauthEvents$ = new Subject<OAuthEvent>();
|
||||
const mockOauthService: Partial<OAuthService> = {
|
||||
clearHashAfterLogin: false,
|
||||
events: oauthEvents$
|
||||
events: oauthEvents$,
|
||||
configure: () => {},
|
||||
hasValidAccessToken: jasmine.createSpy().and.returnValue(true),
|
||||
setupAutomaticSilentRefresh: () => {
|
||||
mockOauthService.silentRefresh();
|
||||
mockOauthService.refreshToken();
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -45,8 +51,10 @@ describe('RedirectAuthService', () => {
|
||||
]
|
||||
});
|
||||
|
||||
service = TestBed.inject(RedirectAuthService);
|
||||
TestBed.inject(OAuthService);
|
||||
service = TestBed.inject(RedirectAuthService);
|
||||
spyOn(service, 'ensureDiscoveryDocument').and.resolveTo(true);
|
||||
mockOauthService.getAccessToken = () => 'access-token';
|
||||
});
|
||||
|
||||
it('should emit event when token_received event is received', () => {
|
||||
@@ -66,4 +74,23 @@ describe('RedirectAuthService', () => {
|
||||
|
||||
expect(onTokenReceivedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call refresh token and silent refresh when automatic silent refresh is setup', async () => {
|
||||
let refreshTokenCalled = false;
|
||||
let silentRefreshCalled = false;
|
||||
|
||||
mockOauthService.refreshToken = async () => {
|
||||
refreshTokenCalled = true;
|
||||
return Promise.resolve({} as TokenResponse);
|
||||
};
|
||||
mockOauthService.silentRefresh = async () => {
|
||||
silentRefreshCalled = true;
|
||||
return Promise.resolve({} as OAuthEvent);
|
||||
};
|
||||
|
||||
await service.init();
|
||||
|
||||
expect(refreshTokenCalled).toBe(true);
|
||||
expect(silentRefreshCalled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Inject, Injectable, inject } from '@angular/core';
|
||||
import { AuthConfig, AUTH_CONFIG, OAuthErrorEvent, OAuthEvent, OAuthService, OAuthStorage, TokenResponse, LoginOptions } from 'angular-oauth2-oidc';
|
||||
import { AuthConfig, AUTH_CONFIG, OAuthErrorEvent, OAuthEvent, OAuthService, OAuthStorage, TokenResponse, LoginOptions, OAuthSuccessEvent } from 'angular-oauth2-oidc';
|
||||
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, shareReplay } from 'rxjs/operators';
|
||||
@@ -169,13 +169,57 @@ export class RedirectAuthService extends AuthService {
|
||||
});
|
||||
}
|
||||
|
||||
return this.ensureDiscoveryDocument().then(() =>
|
||||
void this.oauthService.setupAutomaticSilentRefresh()
|
||||
).catch(() => {
|
||||
return this.ensureDiscoveryDocument().then(() => {
|
||||
this.oauthService.setupAutomaticSilentRefresh();
|
||||
return void this.allowRefreshTokenAndSilentRefreshOnMultipleTabs();
|
||||
}).catch(() => {
|
||||
// catch error to prevent the app from crashing when trying to access unprotected routes
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix a known issue (https://github.com/manfredsteyer/angular-oauth2-oidc/issues/850)
|
||||
* where multiple tabs can cause the token refresh and the silent refresh to fail.
|
||||
* This patch is based on the solutions provided in the following comments:
|
||||
* https://github.com/manfredsteyer/angular-oauth2-oidc/issues/850#issuecomment-889921776 fix silent refresh for the implicit flow
|
||||
* https://github.com/manfredsteyer/angular-oauth2-oidc/issues/850#issuecomment-1557286966 fix refresh token for the code flow
|
||||
*/
|
||||
private allowRefreshTokenAndSilentRefreshOnMultipleTabs() {
|
||||
let lastUpdatedAccessToken: string | undefined;
|
||||
|
||||
if (this.oauthService.hasValidAccessToken()) {
|
||||
lastUpdatedAccessToken = this.oauthService.getAccessToken();
|
||||
}
|
||||
|
||||
const originalRefreshToken = this.oauthService.refreshToken.bind(this.oauthService);
|
||||
this.oauthService.refreshToken = (): Promise<TokenResponse> =>
|
||||
navigator.locks.request(`refresh_tokens_${location.origin}`, () => {
|
||||
if (!!lastUpdatedAccessToken && lastUpdatedAccessToken !== this.oauthService.getAccessToken()) {
|
||||
(this.oauthService as any).eventsSubject.next(new OAuthSuccessEvent('token_received'));
|
||||
(this.oauthService as any).eventsSubject.next(new OAuthSuccessEvent('token_refreshed'));
|
||||
lastUpdatedAccessToken = this.oauthService.getAccessToken();
|
||||
return;
|
||||
}
|
||||
|
||||
return originalRefreshToken().then((resp) => (lastUpdatedAccessToken = resp.access_token));
|
||||
});
|
||||
|
||||
const originalSilentRefresh = this.oauthService.silentRefresh.bind(this.oauthService);
|
||||
this.oauthService.silentRefresh = async (params: any = {}, noPrompt = true): Promise<OAuthEvent> =>
|
||||
navigator.locks.request(`silent_refresh_${location.origin}`, async (): Promise<OAuthEvent> => {
|
||||
if (lastUpdatedAccessToken !== this.oauthService.getAccessToken()) {
|
||||
(this.oauthService as any).eventsSubject.next(new OAuthSuccessEvent('token_received'));
|
||||
(this.oauthService as any).eventsSubject.next(new OAuthSuccessEvent('token_refreshed'));
|
||||
const event = new OAuthSuccessEvent('silently_refreshed');
|
||||
(this.oauthService as any).eventsSubject.next(event);
|
||||
lastUpdatedAccessToken = this.oauthService.getAccessToken();
|
||||
return event;
|
||||
} else {
|
||||
return originalSilentRefresh(params, noPrompt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateIDPConfiguration(config: AuthConfig) {
|
||||
this.oauthService.configure(config);
|
||||
}
|
||||
|
Reference in New Issue
Block a user