Some checks are pending
Build, Lint & Test Lib / Built, Lint and Test Library (push) Waiting to run
Build, Lint & Test Lib / Angular latest (push) Blocked by required conditions
Build, Lint & Test Lib / Angular latest & Schematics Job (push) Blocked by required conditions
Build, Lint & Test Lib / Angular latest Standalone & Schematics Job (push) Blocked by required conditions
Build, Lint & Test Lib / Angular 16 & RxJs 6 (push) Blocked by required conditions
Build, Lint & Test Lib / Angular V16 (push) Blocked by required conditions
Docs / Build and Deploy Docs Job (push) Waiting to run
Docs / Close Pull Request Job (push) Waiting to run
433 lines
15 KiB
TypeScript
433 lines
15 KiB
TypeScript
import { fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
|
|
import { of, throwError } from 'rxjs';
|
|
import { mockProvider } from '../../test/auto-mock';
|
|
import { AuthStateService } from '../auth-state/auth-state.service';
|
|
import { ConfigurationService } from '../config/config.service';
|
|
import { OpenIdConfiguration } from '../config/openid-configuration';
|
|
import { CallbackContext } from '../flows/callback-context';
|
|
import { FlowsDataService } from '../flows/flows-data.service';
|
|
import { ResetAuthDataService } from '../flows/reset-auth-data.service';
|
|
import { RefreshSessionIframeService } from '../iframe/refresh-session-iframe.service';
|
|
import { LoggerService } from '../logging/logger.service';
|
|
import { EventTypes } from '../public-events/event-types';
|
|
import { PublicEventsService } from '../public-events/public-events.service';
|
|
import { StoragePersistenceService } from '../storage/storage-persistence.service';
|
|
import { UserService } from '../user-data/user.service';
|
|
import { FlowHelper } from '../utils/flowHelper/flow-helper.service';
|
|
import { IntervalService } from './interval.service';
|
|
import { PeriodicallyTokenCheckService } from './periodically-token-check.service';
|
|
import { RefreshSessionRefreshTokenService } from './refresh-session-refresh-token.service';
|
|
|
|
describe('PeriodicallyTokenCheckService', () => {
|
|
let periodicallyTokenCheckService: PeriodicallyTokenCheckService;
|
|
let intervalService: IntervalService;
|
|
let flowsDataService: FlowsDataService;
|
|
let flowHelper: FlowHelper;
|
|
let authStateService: AuthStateService;
|
|
let refreshSessionRefreshTokenService: RefreshSessionRefreshTokenService;
|
|
let userService: UserService;
|
|
let storagePersistenceService: StoragePersistenceService;
|
|
let resetAuthDataService: ResetAuthDataService;
|
|
let configurationService: ConfigurationService;
|
|
let publicEventsService: PublicEventsService;
|
|
|
|
beforeEach(() => {
|
|
TestBed.configureTestingModule({
|
|
imports: [],
|
|
providers: [
|
|
mockProvider(ResetAuthDataService),
|
|
FlowHelper,
|
|
mockProvider(FlowsDataService),
|
|
mockProvider(LoggerService),
|
|
mockProvider(UserService),
|
|
mockProvider(AuthStateService),
|
|
mockProvider(RefreshSessionIframeService),
|
|
mockProvider(RefreshSessionRefreshTokenService),
|
|
mockProvider(IntervalService),
|
|
mockProvider(StoragePersistenceService),
|
|
mockProvider(PublicEventsService),
|
|
mockProvider(ConfigurationService),
|
|
],
|
|
});
|
|
});
|
|
|
|
beforeEach(() => {
|
|
periodicallyTokenCheckService = TestBed.inject(
|
|
PeriodicallyTokenCheckService
|
|
);
|
|
intervalService = TestBed.inject(IntervalService);
|
|
flowsDataService = TestBed.inject(FlowsDataService);
|
|
flowHelper = TestBed.inject(FlowHelper);
|
|
authStateService = TestBed.inject(AuthStateService);
|
|
refreshSessionRefreshTokenService = TestBed.inject(
|
|
RefreshSessionRefreshTokenService
|
|
);
|
|
userService = TestBed.inject(UserService);
|
|
storagePersistenceService = TestBed.inject(StoragePersistenceService);
|
|
resetAuthDataService = TestBed.inject(ResetAuthDataService);
|
|
publicEventsService = TestBed.inject(PublicEventsService);
|
|
configurationService = TestBed.inject(ConfigurationService);
|
|
|
|
spyOn(intervalService, 'startPeriodicTokenCheck').and.returnValue(of(null));
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (!!intervalService.runTokenValidationRunning?.unsubscribe) {
|
|
intervalService.runTokenValidationRunning.unsubscribe();
|
|
intervalService.runTokenValidationRunning = null;
|
|
}
|
|
});
|
|
|
|
it('should create', () => {
|
|
expect(periodicallyTokenCheckService).toBeTruthy();
|
|
});
|
|
|
|
describe('startTokenValidationPeriodically', () => {
|
|
it('returns if no config has silentrenew enabled', waitForAsync(() => {
|
|
const configs = [
|
|
{ silentRenew: false, configId: 'configId1' },
|
|
{ silentRenew: false, configId: 'configId2' },
|
|
];
|
|
|
|
const result =
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
|
|
expect(result).toBeUndefined();
|
|
}));
|
|
|
|
it('returns if runTokenValidationRunning', waitForAsync(() => {
|
|
const configs = [{ silentRenew: true, configId: 'configId1' }];
|
|
|
|
spyOn(intervalService, 'isTokenValidationRunning').and.returnValue(true);
|
|
|
|
const result =
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
|
|
expect(result).toBeUndefined();
|
|
}));
|
|
|
|
it('interval calls resetSilentRenewRunning when current flow is CodeFlowWithRefreshTokens', fakeAsync(() => {
|
|
const configs = [
|
|
{ silentRenew: true, configId: 'configId1', tokenRefreshInSeconds: 1 },
|
|
];
|
|
|
|
spyOn(
|
|
periodicallyTokenCheckService as any,
|
|
'shouldStartPeriodicallyCheckForConfig'
|
|
).and.returnValue(true);
|
|
const isCurrentFlowCodeFlowWithRefreshTokensSpy = spyOn(
|
|
flowHelper,
|
|
'isCurrentFlowCodeFlowWithRefreshTokens'
|
|
).and.returnValue(true);
|
|
const resetSilentRenewRunningSpy = spyOn(
|
|
flowsDataService,
|
|
'resetSilentRenewRunning'
|
|
);
|
|
|
|
spyOn(
|
|
refreshSessionRefreshTokenService,
|
|
'refreshSessionWithRefreshTokens'
|
|
).and.returnValue(of({} as CallbackContext));
|
|
spyOn(configurationService, 'getOpenIDConfiguration').and.returnValue(
|
|
of(configs[0])
|
|
);
|
|
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
|
|
tick(1000);
|
|
|
|
intervalService.runTokenValidationRunning?.unsubscribe();
|
|
intervalService.runTokenValidationRunning = null;
|
|
expect(isCurrentFlowCodeFlowWithRefreshTokensSpy).toHaveBeenCalled();
|
|
expect(resetSilentRenewRunningSpy).toHaveBeenCalled();
|
|
}));
|
|
|
|
it('interval calls resetSilentRenewRunning in case of error when current flow is CodeFlowWithRefreshTokens', fakeAsync(() => {
|
|
const configs = [
|
|
{ silentRenew: true, configId: 'configId1', tokenRefreshInSeconds: 1 },
|
|
];
|
|
|
|
spyOn(
|
|
periodicallyTokenCheckService as any,
|
|
'shouldStartPeriodicallyCheckForConfig'
|
|
).and.returnValue(true);
|
|
const resetSilentRenewRunning = spyOn(
|
|
flowsDataService,
|
|
'resetSilentRenewRunning'
|
|
);
|
|
|
|
spyOn(
|
|
flowHelper,
|
|
'isCurrentFlowCodeFlowWithRefreshTokens'
|
|
).and.returnValue(true);
|
|
spyOn(
|
|
refreshSessionRefreshTokenService,
|
|
'refreshSessionWithRefreshTokens'
|
|
).and.returnValue(throwError(() => new Error('error')));
|
|
spyOn(configurationService, 'getOpenIDConfiguration').and.returnValue(
|
|
of(configs[0])
|
|
);
|
|
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
|
|
tick(1000);
|
|
|
|
expect(
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically
|
|
).toThrowError();
|
|
expect(resetSilentRenewRunning).toHaveBeenCalledOnceWith(configs[0]);
|
|
}));
|
|
|
|
it('interval throws silent renew failed event with data in case of an error', fakeAsync(() => {
|
|
const configs = [
|
|
{ silentRenew: true, configId: 'configId1', tokenRefreshInSeconds: 1 },
|
|
];
|
|
|
|
spyOn(
|
|
periodicallyTokenCheckService as any,
|
|
'shouldStartPeriodicallyCheckForConfig'
|
|
).and.returnValue(true);
|
|
spyOn(flowsDataService, 'resetSilentRenewRunning');
|
|
const publicEventsServiceSpy = spyOn(publicEventsService, 'fireEvent');
|
|
|
|
spyOn(
|
|
flowHelper,
|
|
'isCurrentFlowCodeFlowWithRefreshTokens'
|
|
).and.returnValue(true);
|
|
spyOn(
|
|
refreshSessionRefreshTokenService,
|
|
'refreshSessionWithRefreshTokens'
|
|
).and.returnValue(throwError(() => new Error('error')));
|
|
spyOn(configurationService, 'getOpenIDConfiguration').and.returnValue(
|
|
of(configs[0])
|
|
);
|
|
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
|
|
tick(1000);
|
|
|
|
expect(
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically
|
|
).toThrowError();
|
|
expect(publicEventsServiceSpy.calls.allArgs()).toEqual([
|
|
[EventTypes.SilentRenewStarted],
|
|
[EventTypes.SilentRenewFailed, new Error('error')],
|
|
]);
|
|
}));
|
|
|
|
it('calls resetAuthorizationData and returns if no silent renew is configured', fakeAsync(() => {
|
|
const configs = [
|
|
{ silentRenew: true, configId: 'configId1', tokenRefreshInSeconds: 1 },
|
|
];
|
|
|
|
spyOn(
|
|
periodicallyTokenCheckService as any,
|
|
'shouldStartPeriodicallyCheckForConfig'
|
|
).and.returnValue(true);
|
|
|
|
const configSpy = spyOn(configurationService, 'getOpenIDConfiguration');
|
|
const configWithoutSilentRenew = {
|
|
silentRenew: false,
|
|
configId: 'configId1',
|
|
tokenRefreshInSeconds: 1,
|
|
};
|
|
const configWithoutSilentRenew$ = of(configWithoutSilentRenew);
|
|
|
|
configSpy.and.returnValue(configWithoutSilentRenew$);
|
|
|
|
const resetAuthorizationDataSpy = spyOn(
|
|
resetAuthDataService,
|
|
'resetAuthorizationData'
|
|
);
|
|
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
tick(1000);
|
|
intervalService.runTokenValidationRunning?.unsubscribe();
|
|
intervalService.runTokenValidationRunning = null;
|
|
|
|
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
|
|
expect(resetAuthorizationDataSpy).toHaveBeenCalledOnceWith(
|
|
configWithoutSilentRenew,
|
|
configs
|
|
);
|
|
}));
|
|
|
|
it('calls refreshSessionWithRefreshTokens if current flow is Code flow with refresh tokens', fakeAsync(() => {
|
|
spyOn(
|
|
flowHelper,
|
|
'isCurrentFlowCodeFlowWithRefreshTokens'
|
|
).and.returnValue(true);
|
|
spyOn(
|
|
periodicallyTokenCheckService as any,
|
|
'shouldStartPeriodicallyCheckForConfig'
|
|
).and.returnValue(true);
|
|
spyOn(storagePersistenceService, 'read').and.returnValue({});
|
|
const configs = [
|
|
{ configId: 'configId1', silentRenew: true, tokenRefreshInSeconds: 1 },
|
|
];
|
|
|
|
spyOn(configurationService, 'getOpenIDConfiguration').and.returnValue(
|
|
of(configs[0] as OpenIdConfiguration)
|
|
);
|
|
const refreshSessionWithRefreshTokensSpy = spyOn(
|
|
refreshSessionRefreshTokenService,
|
|
'refreshSessionWithRefreshTokens'
|
|
).and.returnValue(of({} as CallbackContext));
|
|
|
|
periodicallyTokenCheckService.startTokenValidationPeriodically(
|
|
configs,
|
|
configs[0]
|
|
);
|
|
|
|
tick(1000);
|
|
|
|
intervalService.runTokenValidationRunning?.unsubscribe();
|
|
intervalService.runTokenValidationRunning = null;
|
|
expect(refreshSessionWithRefreshTokensSpy).toHaveBeenCalled();
|
|
}));
|
|
});
|
|
|
|
describe('shouldStartPeriodicallyCheckForConfig', () => {
|
|
it('returns false when there is no IdToken', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(false);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(
|
|
'some-userdata'
|
|
);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeFalse();
|
|
});
|
|
|
|
it('returns false when silent renew is running', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('idToken');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(true);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(
|
|
'some-userdata'
|
|
);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeFalse();
|
|
});
|
|
|
|
it('returns false when code flow is in progress', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('idToken');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(false);
|
|
spyOn(flowsDataService, 'isCodeFlowInProgress').and.returnValue(true);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(
|
|
'some-userdata'
|
|
);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeFalse();
|
|
});
|
|
|
|
it('returns false when there is no userdata from the store', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('idToken');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(true);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(null);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeFalse();
|
|
});
|
|
|
|
it('returns true when there is userDataFromStore, silentrenew is not running and there is an idtoken', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('idToken');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(false);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(
|
|
'some-userdata'
|
|
);
|
|
|
|
spyOn(
|
|
authStateService,
|
|
'hasIdTokenExpiredAndRenewCheckIsEnabled'
|
|
).and.returnValue(true);
|
|
spyOn(
|
|
authStateService,
|
|
'hasAccessTokenExpiredIfExpiryExists'
|
|
).and.returnValue(true);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeTrue();
|
|
});
|
|
|
|
it('returns false if tokens are not expired', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('idToken');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(false);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(
|
|
'some-userdata'
|
|
);
|
|
spyOn(
|
|
authStateService,
|
|
'hasIdTokenExpiredAndRenewCheckIsEnabled'
|
|
).and.returnValue(false);
|
|
spyOn(
|
|
authStateService,
|
|
'hasAccessTokenExpiredIfExpiryExists'
|
|
).and.returnValue(false);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeFalse();
|
|
});
|
|
|
|
it('returns true if tokens are expired', () => {
|
|
spyOn(authStateService, 'getIdToken').and.returnValue('idToken');
|
|
spyOn(flowsDataService, 'isSilentRenewRunning').and.returnValue(false);
|
|
spyOn(userService, 'getUserDataFromStore').and.returnValue(
|
|
'some-userdata'
|
|
);
|
|
|
|
spyOn(
|
|
authStateService,
|
|
'hasIdTokenExpiredAndRenewCheckIsEnabled'
|
|
).and.returnValue(true);
|
|
spyOn(
|
|
authStateService,
|
|
'hasAccessTokenExpiredIfExpiryExists'
|
|
).and.returnValue(true);
|
|
|
|
const result = (
|
|
periodicallyTokenCheckService as any
|
|
).shouldStartPeriodicallyCheckForConfig({ configId: 'configId1' });
|
|
|
|
expect(result).toBeTrue();
|
|
});
|
|
});
|
|
});
|