fix: fix all biome

This commit is contained in:
2025-01-31 05:57:51 +08:00
parent 316361bd3c
commit c9d0066d64
114 changed files with 2255 additions and 2068 deletions

View File

@@ -1,5 +1,5 @@
import { JwtKeys } from '../validation/jwtkeys';
import { StateValidationResult } from '../validation/state-validation-result';
import type { JwtKeys } from '../validation/jwtkeys';
import type { StateValidationResult } from '../validation/state-validation-result';
export interface CallbackContext {
code: string;

View File

@@ -1,6 +1,6 @@
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
import { HttpErrorResponse, HttpHeaders } from '@ngify/http';
import { of, throwError } from 'rxjs';
import { lastValueFrom, of, throwError } from 'rxjs';
import { vi } from 'vitest';
import { DataService } from '../../api/data.service';
import { LoggerService } from '../../logging/logger.service';
@@ -32,9 +32,6 @@ describe('CodeFlowCallbackHandlerService', () => {
mockProvider(DataService),
],
});
});
beforeEach(() => {
service = TestBed.inject(CodeFlowCallbackHandlerService);
dataService = TestBed.inject(DataService);
urlService = TestBed.inject(UrlService);
@@ -58,13 +55,13 @@ describe('CodeFlowCallbackHandlerService', () => {
() => ''
);
service
.codeFlowCallback('test-url', { configId: 'configId1' })
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.codeFlowCallback('test-url', { configId: 'configId1' })
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('throws error if no code is given', async () => {
@@ -72,15 +69,19 @@ describe('CodeFlowCallbackHandlerService', () => {
.spyOn(urlService, 'getUrlParameter')
.mockReturnValue('params');
getUrlParameterSpy.withArgs('test-url', 'code').mockReturnValue('');
mockImplementationWhenArgsEqual(
getUrlParameterSpy,
['test-url', 'code'],
() => ''
);
service
.codeFlowCallback('test-url', { configId: 'configId1' })
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.codeFlowCallback('test-url', { configId: 'configId1' })
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('returns callbackContext if all params are good', async () => {
@@ -98,9 +99,10 @@ describe('CodeFlowCallbackHandlerService', () => {
existingIdToken: null,
} as CallbackContext;
const callbackContext = await lastValueFrom(service
.codeFlowCallback('test-url', { configId: 'configId1' }));
expect(callbackContext).toEqual(expectedCallbackContext);
const callbackContext = await lastValueFrom(
service.codeFlowCallback('test-url', { configId: 'configId1' })
);
expect(callbackContext).toEqual(expectedCallbackContext);
});
});
@@ -119,13 +121,15 @@ expect(callbackContext).toEqual(expectedCallbackContext);
'validateStateFromHashCallback'
).mockReturnValue(false);
service
.codeFlowCodeRequest({} as CallbackContext, { configId: 'configId1' })
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, {
configId: 'configId1',
})
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('throws error if authWellknownEndpoints is null is given', async () => {
@@ -139,13 +143,15 @@ expect(callbackContext).toEqual(expectedCallbackContext);
() => null
);
service
.codeFlowCodeRequest({} as CallbackContext, { configId: 'configId1' })
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, {
configId: 'configId1',
})
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('throws error if tokenendpoint is null is given', async () => {
@@ -159,13 +165,15 @@ expect(callbackContext).toEqual(expectedCallbackContext);
() => ({ tokenEndpoint: null })
);
service
.codeFlowCodeRequest({} as CallbackContext, { configId: 'configId1' })
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, {
configId: 'configId1',
})
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('calls dataService if all params are good', async () => {
@@ -182,14 +190,17 @@ expect(callbackContext).toEqual(expectedCallbackContext);
'validateStateFromHashCallback'
).mockReturnValue(true);
await lastValueFrom(service
.codeFlowCodeRequest({} as CallbackContext, { configId: 'configId1' }));
expect(postSpy).toHaveBeenCalledExactlyOnceWith(
'tokenEndpoint',
undefined,
{ configId: 'configId1' },
expect.any(HttpHeaders)
);
await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, {
configId: 'configId1',
})
);
expect(postSpy).toHaveBeenCalledExactlyOnceWith(
'tokenEndpoint',
undefined,
{ configId: 'configId1' },
expect.any(HttpHeaders)
);
});
it('calls url service with custom token params', async () => {
@@ -215,12 +226,13 @@ expect(postSpy).toHaveBeenCalledExactlyOnceWith(
const postSpy = vi.spyOn(dataService, 'post').mockReturnValue(of({}));
await lastValueFrom(service
.codeFlowCodeRequest({ code: 'foo' } as CallbackContext, config));
expect(urlServiceSpy).toHaveBeenCalledExactlyOnceWith('foo', config, {
foo: 'bar',
});;
expect(postSpy).toHaveBeenCalledTimes(1);
await lastValueFrom(
service.codeFlowCodeRequest({ code: 'foo' } as CallbackContext, config)
);
expect(urlServiceSpy).toHaveBeenCalledExactlyOnceWith('foo', config, {
foo: 'bar',
});
expect(postSpy).toHaveBeenCalledTimes(1);
});
it('calls dataService with correct headers if all params are good', async () => {
@@ -241,13 +253,14 @@ expect(postSpy).toHaveBeenCalledTimes(1);
'validateStateFromHashCallback'
).mockReturnValue(true);
await lastValueFrom(service
.codeFlowCodeRequest({} as CallbackContext, config));
const httpHeaders = postSpy.calls.mostRecent().args[3] as HttpHeaders;;
expect(httpHeaders.has('Content-Type')).toBeTruthy();;
expect(httpHeaders.get('Content-Type')).toBe(
'application/x-www-form-urlencoded'
);
await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, config)
);
const httpHeaders = postSpy.mock.calls.at(-1)?.[3] as HttpHeaders;
expect(httpHeaders.has('Content-Type')).toBeTruthy();
expect(httpHeaders.get('Content-Type')).toBe(
'application/x-www-form-urlencoded'
);
});
it('returns error in case of http error', async () => {
@@ -266,11 +279,13 @@ expect(httpHeaders.get('Content-Type')).toBe(
() => ({ tokenEndpoint: 'tokenEndpoint' })
);
service.codeFlowCodeRequest({} as CallbackContext, config).subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, config)
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('retries request in case of no connection http error and succeeds', async () => {
@@ -297,16 +312,15 @@ expect(httpHeaders.get('Content-Type')).toBe(
'validateStateFromHashCallback'
).mockReturnValue(true);
service.codeFlowCodeRequest({} as CallbackContext, config).subscribe({
next: (res) => {
expect(res).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
},
error: (err) => {
// fails if there should be a result
expect(err).toBeFalsy();
},
});
try {
const res = await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, config)
);
expect(res).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
} catch (err: any) {
expect(err).toBeFalsy();
}
});
it('retries request in case of no connection http error and fails because of http error afterwards', async () => {
@@ -333,16 +347,15 @@ expect(httpHeaders.get('Content-Type')).toBe(
'validateStateFromHashCallback'
).mockReturnValue(true);
service.codeFlowCodeRequest({} as CallbackContext, config).subscribe({
next: (res) => {
// fails if there should be a result
expect(res).toBeFalsy();
},
error: (err) => {
expect(err).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
},
});
try {
const res = await lastValueFrom(
service.codeFlowCodeRequest({} as CallbackContext, config)
);
expect(res).toBeFalsy();
} catch (err: any) {
expect(err).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
}
});
});
});

View File

@@ -1,14 +1,14 @@
import { HttpHeaders } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Observable, of, throwError, timer } from 'rxjs';
import { type Observable, of, throwError, timer } from 'rxjs';
import { catchError, mergeMap, retryWhen, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';
import { OpenIdConfiguration } from '../../config/openid-configuration';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
import { UrlService } from '../../utils/url/url.service';
import { TokenValidationService } from '../../validation/token-validation.service';
import { AuthResult, CallbackContext } from '../callback-context';
import type { AuthResult, CallbackContext } from '../callback-context';
import { FlowsDataService } from '../flows-data.service';
import { isNetworkError } from './error-helper';

View File

@@ -1,5 +1,5 @@
import { TestBed } from '@/testing';
import { of, throwError } from 'rxjs';
import { lastValueFrom, of, throwError } from 'rxjs';
import { vi } from 'vitest';
import { AuthStateService } from '../../auth-state/auth-state.service';
import { LoggerService } from '../../logging/logger.service';
@@ -83,17 +83,18 @@ describe('HistoryJwtKeysCallbackHandlerService', () => {
vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
of({ keys: [] } as JwtKeys)
);
await lastValueFrom(service
.callbackHistoryAndResetJwtKeys(
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
));
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['jwtKeys', { keys: [] }, allConfigs[0]],
]);;
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
)
);
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['jwtKeys', { keys: [] }, allConfigs[0]],
]);
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
});
it('writes refresh_token into the storage without reuse (refresh token rotation)', async () => {
@@ -120,17 +121,18 @@ expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
of({ keys: [] } as JwtKeys)
);
await lastValueFrom(service
.callbackHistoryAndResetJwtKeys(
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
));
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['jwtKeys', { keys: [] }, allConfigs[0]],
]);;
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
)
);
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['jwtKeys', { keys: [] }, allConfigs[0]],
]);
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
});
it('writes refresh_token into the storage with reuse (without refresh token rotation)', async () => {
@@ -157,18 +159,19 @@ expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
of({ keys: [] } as JwtKeys)
);
await lastValueFrom(service
.callbackHistoryAndResetJwtKeys(
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
));
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['reusable_refresh_token', 'dummy_refresh_token', allConfigs[0]],
['jwtKeys', { keys: [] }, allConfigs[0]],
]);;
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(3);
)
);
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['reusable_refresh_token', 'dummy_refresh_token', allConfigs[0]],
['jwtKeys', { keys: [] }, allConfigs[0]],
]);
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(3);
});
it('resetBrowserHistory if historyCleanup is turned on and is not in a renewProcess', async () => {
@@ -191,13 +194,14 @@ expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(3);
vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
of({ keys: [] } as JwtKeys)
);
await lastValueFrom(service
.callbackHistoryAndResetJwtKeys(
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
));
expect(windowSpy).toHaveBeenCalledTimes(1);
)
);
expect(windowSpy).toHaveBeenCalledTimes(1);
});
it('returns callbackContext with jwtkeys filled if everything works fine', async () => {
@@ -219,17 +223,18 @@ expect(windowSpy).toHaveBeenCalledTimes(1);
vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
of({ keys: [{ kty: 'henlo' } as JwtKey] } as JwtKeys)
);
const result = await lastValueFrom(service
.callbackHistoryAndResetJwtKeys(
const result = await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
));
expect(result).toEqual({
isRenewProcess: false,
authResult: DUMMY_AUTH_RESULT,
jwtKeys: { keys: [{ kty: 'henlo' }] },
} as CallbackContext);
)
);
expect(result).toEqual({
isRenewProcess: false,
authResult: DUMMY_AUTH_RESULT,
jwtKeys: { keys: [{ kty: 'henlo' }] },
} as CallbackContext);
});
it('returns error if no jwtKeys have been in the call --> keys are null', async () => {
@@ -251,19 +256,19 @@ expect(result).toEqual({
vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
of({} as JwtKeys)
);
service
.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
error: (err) => {
expect(err.message).toEqual(
'Failed to retrieve signing key with error: Error: Failed to retrieve signing key'
);
},
});
try {
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch (err: any) {
expect(err.message).toEqual(
'Failed to retrieve signing key with error: Error: Failed to retrieve signing key'
);
}
});
it('returns error if no jwtKeys have been in the call --> keys throw an error', async () => {
@@ -284,19 +289,19 @@ expect(result).toEqual({
vi.spyOn(signInKeyDataService, 'getSigningKeys').mockReturnValue(
throwError(() => new Error('error'))
);
service
.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
error: (err) => {
expect(err.message).toEqual(
'Failed to retrieve signing key with error: Error: Error: error'
);
},
});
try {
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch (err: any) {
expect(err.message).toEqual(
'Failed to retrieve signing key with error: Error: Error: error'
);
}
});
it('returns error if callbackContext.authresult has an error property filled', async () => {
@@ -310,19 +315,19 @@ expect(result).toEqual({
},
];
service
.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
error: (err) => {
expect(err.message).toEqual(
'AuthCallback AuthResult came with error: someError'
);
},
});
try {
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch (err: any) {
expect(err.message).toEqual(
'AuthCallback AuthResult came with error: someError'
);
}
});
it('calls resetAuthorizationData, resets nonce and authStateService in case of an error', async () => {
@@ -347,25 +352,23 @@ expect(result).toEqual({
'updateAndPublishAuthState'
);
service
.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
error: () => {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(setNonceSpy).toHaveBeenCalledTimes(1);
expect(
updateAndPublishAuthStateSpy
).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.SecureTokenServerError,
isRenewProcess: false,
});
},
try {
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(setNonceSpy).toHaveBeenCalledTimes(1);
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.SecureTokenServerError,
isRenewProcess: false,
});
}
});
it('calls authStateService.updateAndPublishAuthState with login required if the error is `login_required`', async () => {
@@ -390,25 +393,23 @@ expect(result).toEqual({
'updateAndPublishAuthState'
);
service
.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
error: () => {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(setNonceSpy).toHaveBeenCalledTimes(1);
expect(
updateAndPublishAuthStateSpy
).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.LoginRequired,
isRenewProcess: false,
});
},
try {
await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
callbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(setNonceSpy).toHaveBeenCalledTimes(1);
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.LoginRequired,
isRenewProcess: false,
});
}
});
it('should store jwtKeys', async () => {
@@ -434,26 +435,23 @@ expect(result).toEqual({
of(DUMMY_JWT_KEYS)
);
service
.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
next: (callbackContext: CallbackContext) => {
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['jwtKeys', DUMMY_JWT_KEYS, allConfigs[0]],
]);
expect(callbackContext.jwtKeys).toEqual(DUMMY_JWT_KEYS);
},
error: (err) => {
expect(err).toBeFalsy();
},
});
try {
const callbackContext: CallbackContext = await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
);
expect(storagePersistenceServiceSpy).toHaveBeenCalledTimes(2);
expect(storagePersistenceServiceSpy).toBeCalledWith([
['authnResult', DUMMY_AUTH_RESULT, allConfigs[0]],
['jwtKeys', DUMMY_JWT_KEYS, allConfigs[0]],
]);
expect(callbackContext.jwtKeys).toEqual(DUMMY_JWT_KEYS);
} catch (err: any) {
expect(err).toBeFalsy();
}
});
it('should not store jwtKeys on error', async () => {
@@ -480,29 +478,23 @@ expect(result).toEqual({
throwError(() => new Error('Error'))
);
service
.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
next: (callbackContext: CallbackContext) => {
expect(callbackContext).toBeFalsy();
},
error: (err) => {
expect(err).toBeTruthy();
// storagePersistenceService.write() should not have been called with jwtKeys
expect(
storagePersistenceServiceSpy
).toHaveBeenCalledExactlyOnceWith(
'authnResult',
authResult,
allConfigs[0]
);
},
});
try {
const callbackContext: CallbackContext = await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
);
expect(callbackContext).toBeFalsy();
} catch (err: any) {
expect(err).toBeTruthy();
expect(storagePersistenceServiceSpy).toHaveBeenCalledExactlyOnceWith(
'authnResult',
authResult,
allConfigs[0]
);
}
});
it('should fallback to stored jwtKeys on error', async () => {
@@ -530,23 +522,22 @@ expect(result).toEqual({
throwError(() => new Error('Error'))
);
service
.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
next: (callbackContext: CallbackContext) => {
expect(
storagePersistenceServiceSpy
).toHaveBeenCalledExactlyOnceWith('jwtKeys', allConfigs[0]);
expect(callbackContext.jwtKeys).toEqual(DUMMY_JWT_KEYS);
},
error: (err) => {
expect(err).toBeFalsy();
},
});
try {
const callbackContext: CallbackContext = await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
);
expect(storagePersistenceServiceSpy).toHaveBeenCalledExactlyOnceWith(
'jwtKeys',
allConfigs[0]
);
expect(callbackContext.jwtKeys).toEqual(DUMMY_JWT_KEYS);
} catch (err: any) {
expect(err).toBeFalsy();
}
});
it('should throw error if no jwtKeys are stored', async () => {
@@ -568,20 +559,18 @@ expect(result).toEqual({
throwError(() => new Error('Error'))
);
service
.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
next: (callbackContext: CallbackContext) => {
expect(callbackContext).toBeFalsy();
},
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
const callbackContext: CallbackContext = await lastValueFrom(
service.callbackHistoryAndResetJwtKeys(
initialCallbackContext,
allConfigs[0]!,
allConfigs
)
);
expect(callbackContext).toBeFalsy();
} catch (err: any) {
expect(err).toBeTruthy();
}
});
});

View File

@@ -98,10 +98,10 @@ export class HistoryJwtKeysCallbackHandlerService {
// fallback: try to load jwtKeys from storage
const storedJwtKeys = this.readSigningKeys(config);
if (!!storedJwtKeys) {
if (storedJwtKeys) {
this.loggerService.logWarning(
config,
`Failed to retrieve signing keys, fallback to stored keys`
'Failed to retrieve signing keys, fallback to stored keys'
);
return of(storedJwtKeys);
@@ -116,7 +116,7 @@ export class HistoryJwtKeysCallbackHandlerService {
return of(callbackContext);
}
const errorMessage = `Failed to retrieve signing key`;
const errorMessage = 'Failed to retrieve signing key';
this.loggerService.logWarning(config, errorMessage);

View File

@@ -1,4 +1,5 @@
import { TestBed } from '@/testing';
import { lastValueFrom } from 'rxjs';
import { vi } from 'vitest';
import { DOCUMENT } from '../../dom';
import { LoggerService } from '../../logging/logger.service';
@@ -57,9 +58,10 @@ describe('ImplicitFlowCallbackHandlerService', () => {
},
];
await lastValueFrom(service
.implicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash'));
expect(resetAuthorizationDataSpy).toHaveBeenCalled();
await lastValueFrom(
service.implicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash')
);
expect(resetAuthorizationDataSpy).toHaveBeenCalled();
});
it('does NOT calls "resetAuthorizationData" if silent renew is running', async () => {
@@ -74,9 +76,10 @@ expect(resetAuthorizationDataSpy).toHaveBeenCalled();
},
];
await lastValueFrom(service
.implicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash'));
expect(resetAuthorizationDataSpy).not.toHaveBeenCalled();
await lastValueFrom(
service.implicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash')
);
expect(resetAuthorizationDataSpy).not.toHaveBeenCalled();
});
it('returns callbackContext if all params are good', async () => {
@@ -99,9 +102,10 @@ expect(resetAuthorizationDataSpy).not.toHaveBeenCalled();
},
];
const callbackContext = await lastValueFrom(service
.implicitFlowCallback(allConfigs[0]!, allConfigs, 'anyHash'));
expect(callbackContext).toEqual(expectedCallbackContext);
const callbackContext = await lastValueFrom(
service.implicitFlowCallback(allConfigs[0]!, allConfigs, 'anyHash')
);
expect(callbackContext).toEqual(expectedCallbackContext);
});
it('uses window location hash if no hash is passed', async () => {
@@ -124,9 +128,10 @@ expect(callbackContext).toEqual(expectedCallbackContext);
},
];
const callbackContext = await lastValueFrom(service
.implicitFlowCallback(allConfigs[0]!, allConfigs));
expect(callbackContext).toEqual(expectedCallbackContext);
const callbackContext = await lastValueFrom(
service.implicitFlowCallback(allConfigs[0]!, allConfigs)
);
expect(callbackContext).toEqual(expectedCallbackContext);
});
});
});

View File

@@ -1,4 +1,5 @@
import { TestBed } from '@/testing';
import { lastValueFrom } from 'rxjs';
import { vi } from 'vitest';
import { AuthStateService } from '../../auth-state/auth-state.service';
import { LoggerService } from '../../logging/logger.service';
@@ -21,9 +22,6 @@ describe('RefreshSessionCallbackHandlerService', () => {
mockProvider(FlowsDataService),
],
});
});
beforeEach(() => {
service = TestBed.inject(RefreshSessionCallbackHandlerService);
flowsDataService = TestBed.inject(FlowsDataService);
authStateService = TestBed.inject(AuthStateService);
@@ -56,9 +54,10 @@ describe('RefreshSessionCallbackHandlerService', () => {
existingIdToken: 'henlo-legger',
} as CallbackContext;
const callbackContext = await lastValueFrom(service
.refreshSessionWithRefreshTokens({ configId: 'configId1' }));
expect(callbackContext).toEqual(expectedCallbackContext);
const callbackContext = await lastValueFrom(
service.refreshSessionWithRefreshTokens({ configId: 'configId1' })
);
expect(callbackContext).toEqual(expectedCallbackContext);
});
it('throws error if no refresh token is given', async () => {
@@ -69,13 +68,13 @@ expect(callbackContext).toEqual(expectedCallbackContext);
vi.spyOn(authStateService, 'getRefreshToken').mockReturnValue('');
vi.spyOn(authStateService, 'getIdToken').mockReturnValue('henlo-legger');
service
.refreshSessionWithRefreshTokens({ configId: 'configId1' })
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.refreshSessionWithRefreshTokens({ configId: 'configId1' })
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
});
});

View File

@@ -1,10 +1,10 @@
import { inject, Injectable } from 'injection-js';
import { Observable, of, throwError } from 'rxjs';
import { Injectable, inject } from 'injection-js';
import { type Observable, of, throwError } from 'rxjs';
import { AuthStateService } from '../../auth-state/auth-state.service';
import { OpenIdConfiguration } from '../../config/openid-configuration';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { TokenValidationService } from '../../validation/token-validation.service';
import { CallbackContext } from '../callback-context';
import type { CallbackContext } from '../callback-context';
import { FlowsDataService } from '../flows-data.service';
@Injectable()
@@ -24,7 +24,7 @@ export class RefreshSessionCallbackHandlerService {
this.loggerService.logDebug(
config,
'RefreshSession created. Adding myautostate: ' + stateData
`RefreshSession created. Adding myautostate: ${stateData}`
);
const refreshToken = this.authStateService.getRefreshToken(config);
const idToken = this.authStateService.getIdToken(config);
@@ -53,12 +53,11 @@ export class RefreshSessionCallbackHandlerService {
);
return of(callbackContext);
} else {
const errorMessage = 'no refresh token found, please login';
this.loggerService.logError(config, errorMessage);
return throwError(() => new Error(errorMessage));
}
const errorMessage = 'no refresh token found, please login';
this.loggerService.logError(config, errorMessage);
return throwError(() => new Error(errorMessage));
}
}

View File

@@ -1,6 +1,6 @@
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
import { HttpErrorResponse, HttpHeaders } from '@ngify/http';
import { of, throwError } from 'rxjs';
import { lastValueFrom, of, throwError } from 'rxjs';
import { vi } from 'vitest';
import { DataService } from '../../api/data.service';
import { LoggerService } from '../../logging/logger.service';
@@ -26,9 +26,6 @@ describe('RefreshTokenCallbackHandlerService', () => {
mockProvider(StoragePersistenceService),
],
});
});
beforeEach(() => {
service = TestBed.inject(RefreshTokenCallbackHandlerService);
storagePersistenceService = TestBed.inject(StoragePersistenceService);
dataService = TestBed.inject(DataService);
@@ -48,13 +45,13 @@ describe('RefreshTokenCallbackHandlerService', () => {
});
it('throws error if no tokenEndpoint is given', async () => {
(service as any)
.refreshTokensRequestTokens({} as CallbackContext)
.subscribe({
error: (err: unknown) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
(service as any).refreshTokensRequestTokens({} as CallbackContext)
);
} catch (err: unknown) {
expect(err).toBeTruthy();
}
});
it('calls data service if all params are good', async () => {
@@ -66,21 +63,22 @@ describe('RefreshTokenCallbackHandlerService', () => {
() => ({ tokenEndpoint: 'tokenEndpoint' })
);
await lastValueFrom(service
.refreshTokensRequestTokens({} as CallbackContext, {
await lastValueFrom(
service.refreshTokensRequestTokens({} as CallbackContext, {
configId: 'configId1',
}));
expect(postSpy).toHaveBeenCalledExactlyOnceWith(
'tokenEndpoint',
undefined,
{ configId: 'configId1' },
expect.any(HttpHeaders)
);;
const httpHeaders = postSpy.calls.mostRecent().args[3] as HttpHeaders;;
expect(httpHeaders.has('Content-Type')).toBeTruthy();;
expect(httpHeaders.get('Content-Type')).toBe(
'application/x-www-form-urlencoded'
);
})
);
expect(postSpy).toHaveBeenCalledExactlyOnceWith(
'tokenEndpoint',
undefined,
{ configId: 'configId1' },
expect.any(HttpHeaders)
);
const httpHeaders = postSpy.mock.calls.at(-1)?.[3] as HttpHeaders;
expect(httpHeaders.has('Content-Type')).toBeTruthy();
expect(httpHeaders.get('Content-Type')).toBe(
'application/x-www-form-urlencoded'
);
});
it('calls data service with correct headers if all params are good', async () => {
@@ -92,15 +90,16 @@ expect(httpHeaders.get('Content-Type')).toBe(
() => ({ tokenEndpoint: 'tokenEndpoint' })
);
await lastValueFrom(service
.refreshTokensRequestTokens({} as CallbackContext, {
await lastValueFrom(
service.refreshTokensRequestTokens({} as CallbackContext, {
configId: 'configId1',
}));
const httpHeaders = postSpy.calls.mostRecent().args[3] as HttpHeaders;;
expect(httpHeaders.has('Content-Type')).toBeTruthy();;
expect(httpHeaders.get('Content-Type')).toBe(
'application/x-www-form-urlencoded'
);
})
);
const httpHeaders = postSpy.mock.calls.at(-1)?.[3] as HttpHeaders;
expect(httpHeaders.has('Content-Type')).toBeTruthy();
expect(httpHeaders.get('Content-Type')).toBe(
'application/x-www-form-urlencoded'
);
});
it('returns error in case of http error', async () => {
@@ -115,13 +114,13 @@ expect(httpHeaders.get('Content-Type')).toBe(
() => ({ tokenEndpoint: 'tokenEndpoint' })
);
service
.refreshTokensRequestTokens({} as CallbackContext, config)
.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(
service.refreshTokensRequestTokens({} as CallbackContext, config)
);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('retries request in case of no connection http error and succeeds', async () => {
@@ -139,18 +138,15 @@ expect(httpHeaders.get('Content-Type')).toBe(
() => ({ tokenEndpoint: 'tokenEndpoint' })
);
service
.refreshTokensRequestTokens({} as CallbackContext, config)
.subscribe({
next: (res) => {
expect(res).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
},
error: (err) => {
// fails if there should be a result
expect(err).toBeFalsy();
},
});
try {
const res = await lastValueFrom(
service.refreshTokensRequestTokens({} as CallbackContext, config)
);
expect(res).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
} catch (err: any) {
expect(err).toBeFalsy();
}
});
it('retries request in case of no connection http error and fails because of http error afterwards', async () => {
@@ -168,18 +164,15 @@ expect(httpHeaders.get('Content-Type')).toBe(
() => ({ tokenEndpoint: 'tokenEndpoint' })
);
service
.refreshTokensRequestTokens({} as CallbackContext, config)
.subscribe({
next: (res) => {
// fails if there should be a result
expect(res).toBeFalsy();
},
error: (err) => {
expect(err).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
},
});
try {
const res = await lastValueFrom(
service.refreshTokensRequestTokens({} as CallbackContext, config)
);
expect(res).toBeFalsy();
} catch (err: any) {
expect(err).toBeTruthy();
expect(postSpy).toHaveBeenCalledTimes(1);
}
});
});
});

View File

@@ -1,13 +1,13 @@
import { HttpHeaders } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Observable, of, throwError, timer } from 'rxjs';
import { type Observable, of, throwError, timer } from 'rxjs';
import { catchError, mergeMap, retryWhen, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';
import { OpenIdConfiguration } from '../../config/openid-configuration';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
import { UrlService } from '../../utils/url/url.service';
import { AuthResult, CallbackContext } from '../callback-context';
import type { AuthResult, CallbackContext } from '../callback-context';
import { isNetworkError } from './error-helper';
@Injectable()

View File

@@ -1,5 +1,5 @@
import { TestBed } from '@/testing';
import { of } from 'rxjs';
import { lastValueFrom, of } from 'rxjs';
import { vi } from 'vitest';
import { AuthStateService } from '../../auth-state/auth-state.service';
import { DOCUMENT } from '../../dom';
@@ -42,9 +42,6 @@ describe('StateValidationCallbackHandlerService', () => {
},
],
});
});
beforeEach(() => {
service = TestBed.inject(StateValidationCallbackHandlerService);
stateValidationService = TestBed.inject(StateValidationService);
loggerService = TestBed.inject(LoggerService);
@@ -69,18 +66,19 @@ describe('StateValidationCallbackHandlerService', () => {
);
const allConfigs = [{ configId: 'configId1' }];
const newCallbackContext = await lastValueFrom(service
.callbackStateValidation(
const newCallbackContext = await lastValueFrom(
service.callbackStateValidation(
{} as CallbackContext,
allConfigs[0]!,
allConfigs
));
expect(newCallbackContext).toEqual({
validationResult: {
idToken: 'idTokenJustForTesting',
authResponseIsValid: true,
},
} as CallbackContext);
)
);
expect(newCallbackContext).toEqual({
validationResult: {
idToken: 'idTokenJustForTesting',
authResponseIsValid: true,
},
} as CallbackContext);
});
it('logs error in case of an error', async () => {
@@ -96,20 +94,20 @@ expect(newCallbackContext).toEqual({
const loggerSpy = vi.spyOn(loggerService, 'logWarning');
const allConfigs = [{ configId: 'configId1' }];
service
.callbackStateValidation(
{} as CallbackContext,
try {
await lastValueFrom(
service.callbackStateValidation(
{} as CallbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch {
expect(loggerSpy).toHaveBeenCalledExactlyOnceWith(
allConfigs[0]!,
allConfigs
)
.subscribe({
error: () => {
expect(loggerSpy).toHaveBeenCalledExactlyOnceWith(
allConfigs[0]!,
'authorizedCallback, token(s) validation failed, resetting. Hash: &anyFakeHash'
);
},
});
'authorizedCallback, token(s) validation failed, resetting. Hash: &anyFakeHash'
);
}
});
it('calls resetAuthDataService.resetAuthorizationData and authStateService.updateAndPublishAuthState in case of an error', async () => {
@@ -133,24 +131,22 @@ expect(newCallbackContext).toEqual({
);
const allConfigs = [{ configId: 'configId1' }];
service
.callbackStateValidation(
{ isRenewProcess: true } as CallbackContext,
allConfigs[0]!,
allConfigs
)
.subscribe({
error: () => {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(
updateAndPublishAuthStateSpy
).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.LoginRequired,
isRenewProcess: true,
});
},
try {
await lastValueFrom(
service.callbackStateValidation(
{ isRenewProcess: true } as CallbackContext,
allConfigs[0]!,
allConfigs
)
);
} catch {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.LoginRequired,
isRenewProcess: true,
});
}
});
});
});

View File

@@ -43,21 +43,20 @@ export class StateValidationCallbackHandlerService {
);
return callbackContext;
} else {
const errorMessage = `authorizedCallback, token(s) validation failed, resetting. Hash: ${this.document.location.hash}`;
this.loggerService.logWarning(configuration, errorMessage);
this.resetAuthDataService.resetAuthorizationData(
configuration,
allConfigs
);
this.publishUnauthorizedState(
callbackContext.validationResult,
callbackContext.isRenewProcess
);
throw new Error(errorMessage);
}
const errorMessage = `authorizedCallback, token(s) validation failed, resetting. Hash: ${this.document.location.hash}`;
this.loggerService.logWarning(configuration, errorMessage);
this.resetAuthDataService.resetAuthorizationData(
configuration,
allConfigs
);
this.publishUnauthorizedState(
callbackContext.validationResult,
callbackContext.isRenewProcess
);
throw new Error(errorMessage);
})
);
}

View File

@@ -1,5 +1,5 @@
import { TestBed } from '@/testing';
import { of } from 'rxjs';
import { lastValueFrom, of } from 'rxjs';
import { vi } from 'vitest';
import { AuthStateService } from '../../auth-state/auth-state.service';
import { LoggerService } from '../../logging/logger.service';
@@ -30,9 +30,6 @@ describe('UserCallbackHandlerService', () => {
mockProvider(ResetAuthDataService),
],
});
});
beforeEach(() => {
service = TestBed.inject(UserCallbackHandlerService);
flowsDataService = TestBed.inject(FlowsDataService);
authStateService = TestBed.inject(AuthStateService);
@@ -73,10 +70,11 @@ describe('UserCallbackHandlerService', () => {
const spy = vi.spyOn(flowsDataService, 'setSessionState');
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(spy).toHaveBeenCalledExactlyOnceWith('mystate', allConfigs[0]);;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(spy).toHaveBeenCalledExactlyOnceWith('mystate', allConfigs[0]);
expect(resultCallbackContext).toEqual(callbackContext);
});
it('does NOT call flowsDataService.setSessionState if autoUserInfo is false, isRenewProcess is true and refreshToken is null', async () => {
@@ -105,10 +103,11 @@ expect(resultCallbackContext).toEqual(callbackContext);
];
const spy = vi.spyOn(flowsDataService, 'setSessionState');
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(spy).not.toHaveBeenCalled();;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(spy).not.toHaveBeenCalled();
expect(resultCallbackContext).toEqual(callbackContext);
});
it('does NOT call flowsDataService.setSessionState if autoUserInfo is false isRenewProcess is false, refreshToken has value', async () => {
@@ -137,10 +136,11 @@ expect(resultCallbackContext).toEqual(callbackContext);
];
const spy = vi.spyOn(flowsDataService, 'setSessionState');
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(spy).not.toHaveBeenCalled();;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(spy).not.toHaveBeenCalled();
expect(resultCallbackContext).toEqual(callbackContext);
});
it('does NOT call flowsDataService.setSessionState if autoUserInfo is false isRenewProcess is false, refreshToken has value, id_token is false', async () => {
@@ -165,10 +165,11 @@ expect(resultCallbackContext).toEqual(callbackContext);
const spy = vi.spyOn(flowsDataService, 'setSessionState');
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(spy).not.toHaveBeenCalled();;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(spy).not.toHaveBeenCalled();
expect(resultCallbackContext).toEqual(callbackContext);
});
it('calls authStateService.updateAndPublishAuthState with correct params if autoUserInfo is false', async () => {
@@ -202,14 +203,15 @@ expect(resultCallbackContext).toEqual(callbackContext);
'updateAndPublishAuthState'
);
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: true,
validationResult: ValidationResult.NotSet,
isRenewProcess: false,
});;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: true,
validationResult: ValidationResult.NotSet,
isRenewProcess: false,
});
expect(resultCallbackContext).toEqual(callbackContext);
});
it('calls userService.getAndPersistUserDataInStore with correct params if autoUserInfo is true', async () => {
@@ -242,18 +244,17 @@ expect(resultCallbackContext).toEqual(callbackContext);
.spyOn(userService, 'getAndPersistUserDataInStore')
.mockReturnValue(of({ user: 'some_data' }));
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(
getAndPersistUserDataInStoreSpy
).toHaveBeenCalledExactlyOnceWith(
allConfigs[0]!,
allConfigs,
false,
'idtoken',
'decoded'
);;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(getAndPersistUserDataInStoreSpy).toHaveBeenCalledExactlyOnceWith(
allConfigs[0]!,
allConfigs,
false,
'idtoken',
'decoded'
);
expect(resultCallbackContext).toEqual(callbackContext);
});
it('calls authStateService.updateAndPublishAuthState with correct params if autoUserInfo is true', async () => {
@@ -291,14 +292,15 @@ expect(resultCallbackContext).toEqual(callbackContext);
'updateAndPublishAuthState'
);
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: true,
validationResult: ValidationResult.MaxOffsetExpired,
isRenewProcess: false,
});;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: true,
validationResult: ValidationResult.MaxOffsetExpired,
isRenewProcess: false,
});
expect(resultCallbackContext).toEqual(callbackContext);
});
it('calls flowsDataService.setSessionState with correct params if user data is present and NOT refresh token', async () => {
@@ -333,13 +335,14 @@ expect(resultCallbackContext).toEqual(callbackContext);
);
const setSessionStateSpy = vi.spyOn(flowsDataService, 'setSessionState');
const resultCallbackContext = await lastValueFrom(service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs));
expect(setSessionStateSpy).toHaveBeenCalledExactlyOnceWith(
'mystate',
allConfigs[0]
);;
expect(resultCallbackContext).toEqual(callbackContext);
const resultCallbackContext = await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
expect(setSessionStateSpy).toHaveBeenCalledExactlyOnceWith(
'mystate',
allConfigs[0]
);
expect(resultCallbackContext).toEqual(callbackContext);
});
it('calls authStateService.publishUnauthorizedState with correct params if user info which are coming back are null', async () => {
@@ -377,22 +380,20 @@ expect(resultCallbackContext).toEqual(callbackContext);
'updateAndPublishAuthState'
);
service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
.subscribe({
error: (err) => {
expect(
updateAndPublishAuthStateSpy
).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.MaxOffsetExpired,
isRenewProcess: false,
});
expect(err.message).toEqual(
'Failed to retrieve user info with error: Error: Called for userData but they were null'
);
},
try {
await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
} catch (err: any) {
expect(updateAndPublishAuthStateSpy).toHaveBeenCalledExactlyOnceWith({
isAuthenticated: false,
validationResult: ValidationResult.MaxOffsetExpired,
isRenewProcess: false,
});
expect(err.message).toEqual(
'Failed to retrieve user info with error: Error: Called for userData but they were null'
);
}
});
it('calls resetAuthDataService.resetAuthorizationData if user info which are coming back are null', async () => {
@@ -430,16 +431,16 @@ expect(resultCallbackContext).toEqual(callbackContext);
'resetAuthorizationData'
);
service
.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
.subscribe({
error: (err) => {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(err.message).toEqual(
'Failed to retrieve user info with error: Error: Called for userData but they were null'
);
},
});
try {
await lastValueFrom(
service.callbackUser(callbackContext, allConfigs[0]!, allConfigs)
);
} catch (err: any) {
expect(resetAuthorizationDataSpy).toHaveBeenCalledTimes(1);
expect(err.message).toEqual(
'Failed to retrieve user info with error: Error: Called for userData but they were null'
);
}
});
});
});

View File

@@ -35,6 +35,7 @@ export class UserCallbackHandlerService {
if (!autoUserInfo) {
if (!isRenewProcess || renewUserInfoAfterTokenRenew) {
// userData is set to the id_token decoded, auto get user data set to false
// biome-ignore lint/nursery/useCollapsedIf: <explanation>
if (validationResult?.decodedIdToken) {
this.userService.setUserDataToStore(
validationResult.decodedIdToken,
@@ -66,7 +67,7 @@ export class UserCallbackHandlerService {
)
.pipe(
switchMap((userData) => {
if (!!userData) {
if (userData) {
if (!refreshToken) {
this.flowsDataService.setSessionState(
authResult?.session_state,
@@ -77,18 +78,17 @@ export class UserCallbackHandlerService {
this.publishAuthState(validationResult, isRenewProcess);
return of(callbackContext);
} else {
this.resetAuthDataService.resetAuthorizationData(
configuration,
allConfigs
);
this.publishUnauthenticatedState(validationResult, isRenewProcess);
const errorMessage = `Called for userData but they were ${userData}`;
this.loggerService.logWarning(configuration, errorMessage);
return throwError(() => new Error(errorMessage));
}
this.resetAuthDataService.resetAuthorizationData(
configuration,
allConfigs
);
this.publishUnauthenticatedState(validationResult, isRenewProcess);
const errorMessage = `Called for userData but they were ${userData}`;
this.loggerService.logWarning(configuration, errorMessage);
return throwError(() => new Error(errorMessage));
}),
catchError((err) => {
const errorMessage = `Failed to retrieve user info with error: ${err}`;

View File

@@ -1,4 +1,4 @@
import { TestBed } from '@/testing';
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
import { vi } from 'vitest';
import { LoggerService } from '../logging/logger.service';
import { StoragePersistenceService } from '../storage/storage-persistence.service';
@@ -21,15 +21,13 @@ describe('Flows Data Service', () => {
mockProvider(StoragePersistenceService),
],
});
});
beforeEach(() => {
service = TestBed.inject(FlowsDataService);
storagePersistenceService = TestBed.inject(StoragePersistenceService);
});
// biome-ignore lint/correctness/noUndeclaredVariables: <explanation>
afterEach(() => {
jasmine.clock().uninstall();
vi.useRealTimers();
});
it('should create', () => {
@@ -141,10 +139,11 @@ describe('Flows Data Service', () => {
describe('codeVerifier', () => {
it('getCodeVerifier returns value from the store', () => {
const spy = vi
.spyOn(storagePersistenceService, 'read')
.withArgs('codeVerifier', { configId: 'configId1' })
.mockReturnValue('Genesis');
const spy = mockImplementationWhenArgsEqual(
vi.spyOn(storagePersistenceService, 'read'),
['codeVerifier', { configId: 'configId1' }],
() => 'Genesis'
);
const result = service.getCodeVerifier({ configId: 'configId1' });
@@ -173,11 +172,12 @@ describe('Flows Data Service', () => {
configId: 'configId1',
};
jasmine.clock().uninstall();
jasmine.clock().install();
vi.useRealTimers();
vi.useFakeTimers();
const baseTime = new Date();
jasmine.clock().mockDate(baseTime);
vi.setSystemTime(baseTime);
mockImplementationWhenArgsEqual(
vi.spyOn(storagePersistenceService, 'read'),
@@ -212,11 +212,11 @@ describe('Flows Data Service', () => {
describe('setCodeFlowInProgress', () => {
it('set setCodeFlowInProgress to `in progress` when called', () => {
jasmine.clock().uninstall();
jasmine.clock().install();
vi.useRealTimers();
vi.useFakeTimers();
const baseTime = new Date();
jasmine.clock().mockDate(baseTime);
vi.setSystemTime(baseTime);
const spy = vi.spyOn(storagePersistenceService, 'write');
@@ -253,23 +253,27 @@ describe('Flows Data Service', () => {
configId: 'configId1',
};
jasmine.clock().uninstall();
jasmine.clock().install();
vi.useRealTimers();
vi.useFakeTimers();
const baseTime = new Date();
jasmine.clock().mockDate(baseTime);
vi.setSystemTime(baseTime);
const storageObject = {
state: 'running',
dateOfLaunchedProcessUtc: baseTime.toISOString(),
};
vi.spyOn(storagePersistenceService, 'read')
.withArgs('storageSilentRenewRunning', config)
.mockReturnValue(JSON.stringify(storageObject));
mockImplementationWhenArgsEqual(
vi.spyOn(storagePersistenceService, 'read'),
['storageSilentRenewRunning', config],
() => JSON.stringify(storageObject)
);
const spyWrite = vi.spyOn(storagePersistenceService, 'write');
jasmine.clock().tick((config.silentRenewTimeoutInSeconds + 1) * 1000);
vi.advanceTimersByTimeAsync(
(config.silentRenewTimeoutInSeconds + 1) * 1000
);
const isSilentRenewRunningResult = service.isSilentRenewRunning(config);
@@ -287,20 +291,22 @@ describe('Flows Data Service', () => {
configId: 'configId1',
};
jasmine.clock().uninstall();
jasmine.clock().install();
vi.useRealTimers();
vi.useFakeTimers();
const baseTime = new Date();
jasmine.clock().mockDate(baseTime);
vi.setSystemTime(baseTime);
const storageObject = {
state: 'running',
dateOfLaunchedProcessUtc: baseTime.toISOString(),
};
vi.spyOn(storagePersistenceService, 'read')
.withArgs('storageSilentRenewRunning', config)
.mockReturnValue(JSON.stringify(storageObject));
mockImplementationWhenArgsEqual(
vi.spyOn(storagePersistenceService, 'read'),
['storageSilentRenewRunning', config],
() => JSON.stringify(storageObject)
);
const spyWrite = vi.spyOn(storagePersistenceService, 'write');
const isSilentRenewRunningResult = service.isSilentRenewRunning(config);
@@ -326,11 +332,11 @@ describe('Flows Data Service', () => {
describe('setSilentRenewRunning', () => {
it('set setSilentRenewRunning to `running` with lauched time when called', () => {
jasmine.clock().uninstall();
jasmine.clock().install();
vi.useRealTimers();
vi.useFakeTimers();
const baseTime = new Date();
jasmine.clock().mockDate(baseTime);
vi.setSystemTime(baseTime);
const storageObject = {
state: 'running',

View File

@@ -1,5 +1,5 @@
import { TestBed } from '@/testing';
import { of } from 'rxjs';
import { lastValueFrom, of } from 'rxjs';
import { vi } from 'vitest';
import { mockProvider } from '../testing/mock';
import type { CallbackContext } from './callback-context';
@@ -35,9 +35,6 @@ describe('Flows Service', () => {
mockProvider(RefreshTokenCallbackHandlerService),
],
});
});
beforeEach(() => {
service = TestBed.inject(FlowsService);
codeFlowCallbackHandlerService = TestBed.inject(
CodeFlowCallbackHandlerService
@@ -90,17 +87,22 @@ describe('Flows Service', () => {
},
];
const value = await lastValueFrom(service
.processCodeFlowCallback('some-url1234', allConfigs[0]!, allConfigs));
expect(value).toEqual({} as CallbackContext);;
expect(codeFlowCallbackSpy).toHaveBeenCalledExactlyOnceWith(
'some-url1234',
allConfigs[0]
);;
expect(codeFlowCodeRequestSpy).toHaveBeenCalledTimes(1);;
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalledTimes(1);;
expect(callbackStateValidationSpy).toHaveBeenCalledTimes(1);;
expect(callbackUserSpy).toHaveBeenCalledTimes(1);
const value = await lastValueFrom(
service.processCodeFlowCallback(
'some-url1234',
allConfigs[0]!,
allConfigs
)
);
expect(value).toEqual({} as CallbackContext);
expect(codeFlowCallbackSpy).toHaveBeenCalledExactlyOnceWith(
'some-url1234',
allConfigs[0]
);
expect(codeFlowCodeRequestSpy).toHaveBeenCalledTimes(1);
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalledTimes(1);
expect(callbackStateValidationSpy).toHaveBeenCalledTimes(1);
expect(callbackUserSpy).toHaveBeenCalledTimes(1);
});
});
@@ -127,17 +129,18 @@ expect(callbackUserSpy).toHaveBeenCalledTimes(1);
},
];
const value = await lastValueFrom(service
.processSilentRenewCodeFlowCallback(
const value = await lastValueFrom(
service.processSilentRenewCodeFlowCallback(
{} as CallbackContext,
allConfigs[0]!,
allConfigs
));
expect(value).toEqual({} as CallbackContext);;
expect(codeFlowCodeRequestSpy).toHaveBeenCalled();;
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalled();;
expect(callbackStateValidationSpy).toHaveBeenCalled();;
expect(callbackUserSpy).toHaveBeenCalled();
)
);
expect(value).toEqual({} as CallbackContext);
expect(codeFlowCodeRequestSpy).toHaveBeenCalled();
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalled();
expect(callbackStateValidationSpy).toHaveBeenCalled();
expect(callbackUserSpy).toHaveBeenCalled();
});
});
@@ -164,13 +167,18 @@ expect(callbackUserSpy).toHaveBeenCalled();
},
];
const value = await lastValueFrom(service
.processImplicitFlowCallback(allConfigs[0]!, allConfigs, 'any-hash'));
expect(value).toEqual({} as CallbackContext);;
expect(implicitFlowCallbackSpy).toHaveBeenCalled();;
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalled();;
expect(callbackStateValidationSpy).toHaveBeenCalled();;
expect(callbackUserSpy).toHaveBeenCalled();
const value = await lastValueFrom(
service.processImplicitFlowCallback(
allConfigs[0]!,
allConfigs,
'any-hash'
)
);
expect(value).toEqual({} as CallbackContext);
expect(implicitFlowCallbackSpy).toHaveBeenCalled();
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalled();
expect(callbackStateValidationSpy).toHaveBeenCalled();
expect(callbackUserSpy).toHaveBeenCalled();
});
});
@@ -203,14 +211,15 @@ expect(callbackUserSpy).toHaveBeenCalled();
},
];
const value = await lastValueFrom(service
.processRefreshToken(allConfigs[0]!, allConfigs));
expect(value).toEqual({} as CallbackContext);;
expect(refreshSessionWithRefreshTokensSpy).toHaveBeenCalled();;
expect(refreshTokensRequestTokensSpy).toHaveBeenCalled();;
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalled();;
expect(callbackStateValidationSpy).toHaveBeenCalled();;
expect(callbackUserSpy).toHaveBeenCalled();
const value = await lastValueFrom(
service.processRefreshToken(allConfigs[0]!, allConfigs)
);
expect(value).toEqual({} as CallbackContext);
expect(refreshSessionWithRefreshTokensSpy).toHaveBeenCalled();
expect(refreshTokensRequestTokensSpy).toHaveBeenCalled();
expect(callbackHistoryAndResetJwtKeysSpy).toHaveBeenCalled();
expect(callbackStateValidationSpy).toHaveBeenCalled();
expect(callbackUserSpy).toHaveBeenCalled();
});
});
});

View File

@@ -1,5 +1,4 @@
import { TestBed } from '@/testing';
import { vi } from 'vitest';
import { LoggerService } from '../../logging/logger.service';
import { mockProvider } from '../../testing/mock';
import { CryptoService } from '../../utils/crypto/crypto.service';
@@ -12,9 +11,6 @@ describe('RandomService Tests', () => {
TestBed.configureTestingModule({
providers: [RandomService, mockProvider(LoggerService), CryptoService],
});
});
beforeEach(() => {
randomService = TestBed.inject(RandomService);
});

View File

@@ -1,5 +1,5 @@
import { inject, Injectable } from 'injection-js';
import { OpenIdConfiguration } from '../../config/openid-configuration';
import { Injectable, inject } from 'injection-js';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { CryptoService } from '../../utils/crypto/crypto.service';
@@ -37,7 +37,7 @@ export class RandomService {
}
private toHex(dec: number): string {
return ('0' + dec.toString(16)).substr(-2);
return `0${dec.toString(16)}`.substr(-2);
}
private randomString(length: number): string {

View File

@@ -23,9 +23,6 @@ describe('ResetAuthDataService', () => {
mockProvider(LoggerService),
],
});
});
beforeEach(() => {
service = TestBed.inject(ResetAuthDataService);
userService = TestBed.inject(UserService);
flowsDataService = TestBed.inject(FlowsDataService);

View File

@@ -1,6 +1,6 @@
import { TestBed, mockImplementationWhenArgsEqual } from '@/testing';
import { HttpResponse } from '@ngify/http';
import { isObservable, of, throwError } from 'rxjs';
import { EmptyError, isObservable, lastValueFrom, of, throwError } from 'rxjs';
import { vi } from 'vitest';
import { DataService } from '../api/data.service';
import { LoggerService } from '../logging/logger.service';
@@ -40,9 +40,6 @@ describe('Signin Key Data Service', () => {
mockProvider(StoragePersistenceService),
],
});
});
beforeEach(() => {
service = TestBed.inject(SigninKeyDataService);
storagePersistenceService = TestBed.inject(StoragePersistenceService);
dataService = TestBed.inject(DataService);
@@ -62,11 +59,11 @@ describe('Signin Key Data Service', () => {
);
const result = service.getSigningKeys({ configId: 'configId1' });
result.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(result);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('throws error when no jwksUri given', async () => {
@@ -77,11 +74,11 @@ describe('Signin Key Data Service', () => {
);
const result = service.getSigningKeys({ configId: 'configId1' });
result.subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(result);
} catch (err: any) {
expect(err).toBeTruthy();
}
});
it('calls dataservice if jwksurl is given', async () => {
@@ -94,13 +91,15 @@ describe('Signin Key Data Service', () => {
const result = service.getSigningKeys({ configId: 'configId1' });
result.subscribe({
complete: () => {
try {
await lastValueFrom(result);
} catch (err: any) {
if (err instanceof EmptyError) {
expect(spy).toHaveBeenCalledExactlyOnceWith('someUrl', {
configId: 'configId1',
});
},
});
}
}
});
it('should retry once', async () => {
@@ -116,12 +115,11 @@ describe('Signin Key Data Service', () => {
)
);
service.getSigningKeys({ configId: 'configId1' }).subscribe({
next: (res) => {
expect(res).toBeTruthy();
expect(res).toEqual(DUMMY_JWKS);
},
});
const res = await lastValueFrom(
service.getSigningKeys({ configId: 'configId1' })
);
expect(res).toBeTruthy();
expect(res).toEqual(DUMMY_JWKS);
});
it('should retry twice', async () => {
@@ -138,12 +136,11 @@ describe('Signin Key Data Service', () => {
)
);
service.getSigningKeys({ configId: 'configId1' }).subscribe({
next: (res) => {
expect(res).toBeTruthy();
expect(res).toEqual(DUMMY_JWKS);
},
});
const res = await lastValueFrom(
service.getSigningKeys({ configId: 'configId1' })
);
expect(res).toBeTruthy();
expect(res).toEqual(DUMMY_JWKS);
});
it('should fail after three tries', async () => {
@@ -161,16 +158,16 @@ describe('Signin Key Data Service', () => {
)
);
service.getSigningKeys({ configId: 'configId1' }).subscribe({
error: (err) => {
expect(err).toBeTruthy();
},
});
try {
await lastValueFrom(service.getSigningKeys({ configId: 'configId1' }));
} catch (err: any) {
expect(err).toBeTruthy();
}
});
});
describe('handleErrorGetSigningKeys', () => {
it('keeps observable if error is catched', async () => {
it('keeps observable if error is catched', () => {
const result = (service as any).handleErrorGetSigningKeys(
new HttpResponse()
);
@@ -182,52 +179,54 @@ describe('Signin Key Data Service', () => {
it('logs error if error is response', async () => {
const logSpy = vi.spyOn(loggerService, 'logError');
(service as any)
.handleErrorGetSigningKeys(
new HttpResponse({ status: 400, statusText: 'nono' }),
{ configId: 'configId1' }
)
.subscribe({
error: () => {
expect(logSpy).toHaveBeenCalledExactlyOnceWith(
{ configId: 'configId1' },
'400 - nono {}'
);
},
});
try {
await lastValueFrom(
(service as any).handleErrorGetSigningKeys(
new HttpResponse({ status: 400, statusText: 'nono' }),
{ configId: 'configId1' }
)
);
} catch {
expect(logSpy).toHaveBeenCalledExactlyOnceWith(
{ configId: 'configId1' },
'400 - nono {}'
);
}
});
it('logs error if error is not a response', async () => {
const logSpy = vi.spyOn(loggerService, 'logError');
(service as any)
.handleErrorGetSigningKeys('Just some Error', { configId: 'configId1' })
.subscribe({
error: () => {
expect(logSpy).toHaveBeenCalledExactlyOnceWith(
{ configId: 'configId1' },
'Just some Error'
);
},
});
try {
await lastValueFrom(
(service as any).handleErrorGetSigningKeys('Just some Error', {
configId: 'configId1',
})
);
} catch {
expect(logSpy).toHaveBeenCalledExactlyOnceWith(
{ configId: 'configId1' },
'Just some Error'
);
}
});
it('logs error if error with message property is not a response', async () => {
const logSpy = vi.spyOn(loggerService, 'logError');
(service as any)
.handleErrorGetSigningKeys(
{ message: 'Just some Error' },
{ configId: 'configId1' }
)
.subscribe({
error: () => {
expect(logSpy).toHaveBeenCalledExactlyOnceWith(
{ configId: 'configId1' },
'Just some Error'
);
},
});
try {
await lastValueFrom(
(service as any).handleErrorGetSigningKeys(
{ message: 'Just some Error' },
{ configId: 'configId1' }
)
);
} catch {
expect(logSpy).toHaveBeenCalledExactlyOnceWith(
{ configId: 'configId1' },
'Just some Error'
);
}
});
});
});

View File

@@ -1,12 +1,12 @@
import { HttpResponse } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { Observable, throwError } from 'rxjs';
import { type Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { DataService } from '../api/data.service';
import { OpenIdConfiguration } from '../config/openid-configuration';
import type { OpenIdConfiguration } from '../config/openid-configuration';
import { LoggerService } from '../logging/logger.service';
import { StoragePersistenceService } from '../storage/storage-persistence.service';
import { JwtKeys } from '../validation/jwtkeys';
import type { JwtKeys } from '../validation/jwtkeys';
@Injectable()
export class SigninKeyDataService {
@@ -62,7 +62,7 @@ export class SigninKeyDataService {
} else {
const { message } = errorResponse;
errMsg = !!message ? message : `${errorResponse}`;
errMsg = message ? message : `${errorResponse}`;
}
this.loggerService.logError(currentConfiguration, errMsg);