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
187 lines
5.6 KiB
TypeScript
187 lines
5.6 KiB
TypeScript
import { DOCUMENT } from '../../dom';
|
|
import { inject, Injectable } from 'injection-js';
|
|
import { Observable, of, throwError } from 'rxjs';
|
|
import { catchError, switchMap, tap } from 'rxjs/operators';
|
|
import { AuthStateService } from '../../auth-state/auth-state.service';
|
|
import { OpenIdConfiguration } from '../../config/openid-configuration';
|
|
import { LoggerService } from '../../logging/logger.service';
|
|
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
|
|
import { JwtKeys } from '../../validation/jwtkeys';
|
|
import { ValidationResult } from '../../validation/validation-result';
|
|
import { CallbackContext } from '../callback-context';
|
|
import { FlowsDataService } from '../flows-data.service';
|
|
import { ResetAuthDataService } from '../reset-auth-data.service';
|
|
import { SigninKeyDataService } from '../signin-key-data.service';
|
|
|
|
const JWT_KEYS = 'jwtKeys';
|
|
|
|
@Injectable()
|
|
export class HistoryJwtKeysCallbackHandlerService {
|
|
private readonly loggerService = inject(LoggerService);
|
|
|
|
private readonly authStateService = inject(AuthStateService);
|
|
|
|
private readonly flowsDataService = inject(FlowsDataService);
|
|
|
|
private readonly signInKeyDataService = inject(SigninKeyDataService);
|
|
|
|
private readonly storagePersistenceService = inject(
|
|
StoragePersistenceService
|
|
);
|
|
|
|
private readonly resetAuthDataService = inject(ResetAuthDataService);
|
|
|
|
private readonly document = inject(DOCUMENT);
|
|
|
|
// STEP 3 Code Flow, STEP 2 Implicit Flow, STEP 3 Refresh Token
|
|
callbackHistoryAndResetJwtKeys(
|
|
callbackContext: CallbackContext,
|
|
config: OpenIdConfiguration,
|
|
allConfigs: OpenIdConfiguration[]
|
|
): Observable<CallbackContext> {
|
|
let toWrite = { ...callbackContext.authResult };
|
|
|
|
if (!this.responseHasIdToken(callbackContext)) {
|
|
const existingIdToken = this.storagePersistenceService.getIdToken(config);
|
|
|
|
toWrite = {
|
|
...toWrite,
|
|
id_token: existingIdToken,
|
|
};
|
|
}
|
|
|
|
this.storagePersistenceService.write('authnResult', toWrite, config);
|
|
|
|
if (
|
|
config.allowUnsafeReuseRefreshToken &&
|
|
callbackContext.authResult?.refresh_token
|
|
) {
|
|
this.storagePersistenceService.write(
|
|
'reusable_refresh_token',
|
|
callbackContext.authResult.refresh_token,
|
|
config
|
|
);
|
|
}
|
|
|
|
if (
|
|
this.historyCleanUpTurnedOn(config) &&
|
|
!callbackContext.isRenewProcess
|
|
) {
|
|
this.resetBrowserHistory();
|
|
} else {
|
|
this.loggerService.logDebug(config, 'history clean up inactive');
|
|
}
|
|
|
|
if (callbackContext.authResult?.error) {
|
|
const errorMessage = `AuthCallback AuthResult came with error: ${callbackContext.authResult.error}`;
|
|
|
|
this.loggerService.logDebug(config, errorMessage);
|
|
this.resetAuthDataService.resetAuthorizationData(config, allConfigs);
|
|
this.flowsDataService.setNonce('', config);
|
|
this.handleResultErrorFromCallback(
|
|
callbackContext.authResult,
|
|
callbackContext.isRenewProcess
|
|
);
|
|
|
|
return throwError(() => new Error(errorMessage));
|
|
}
|
|
|
|
this.loggerService.logDebug(
|
|
config,
|
|
`AuthResult '${JSON.stringify(callbackContext.authResult, null, 2)}'.
|
|
AuthCallback created, begin token validation`
|
|
);
|
|
|
|
return this.signInKeyDataService.getSigningKeys(config).pipe(
|
|
tap((jwtKeys: JwtKeys) => this.storeSigningKeys(jwtKeys, config)),
|
|
catchError((err) => {
|
|
// fallback: try to load jwtKeys from storage
|
|
const storedJwtKeys = this.readSigningKeys(config);
|
|
|
|
if (!!storedJwtKeys) {
|
|
this.loggerService.logWarning(
|
|
config,
|
|
`Failed to retrieve signing keys, fallback to stored keys`
|
|
);
|
|
|
|
return of(storedJwtKeys);
|
|
}
|
|
|
|
return throwError(() => new Error(err));
|
|
}),
|
|
switchMap((jwtKeys) => {
|
|
if (jwtKeys) {
|
|
callbackContext.jwtKeys = jwtKeys;
|
|
|
|
return of(callbackContext);
|
|
}
|
|
|
|
const errorMessage = `Failed to retrieve signing key`;
|
|
|
|
this.loggerService.logWarning(config, errorMessage);
|
|
|
|
return throwError(() => new Error(errorMessage));
|
|
}),
|
|
catchError((err) => {
|
|
const errorMessage = `Failed to retrieve signing key with error: ${err}`;
|
|
|
|
this.loggerService.logWarning(config, errorMessage);
|
|
|
|
return throwError(() => new Error(errorMessage));
|
|
})
|
|
);
|
|
}
|
|
|
|
private responseHasIdToken(callbackContext: CallbackContext): boolean {
|
|
return !!callbackContext?.authResult?.id_token;
|
|
}
|
|
|
|
private handleResultErrorFromCallback(
|
|
result: unknown,
|
|
isRenewProcess: boolean
|
|
): void {
|
|
let validationResult = ValidationResult.SecureTokenServerError;
|
|
|
|
if (
|
|
result &&
|
|
typeof result === 'object' &&
|
|
'error' in result &&
|
|
(result.error as string) === 'login_required'
|
|
) {
|
|
validationResult = ValidationResult.LoginRequired;
|
|
}
|
|
|
|
this.authStateService.updateAndPublishAuthState({
|
|
isAuthenticated: false,
|
|
validationResult,
|
|
isRenewProcess,
|
|
});
|
|
}
|
|
|
|
private historyCleanUpTurnedOn(config: OpenIdConfiguration): boolean {
|
|
const { historyCleanupOff } = config;
|
|
|
|
return !historyCleanupOff;
|
|
}
|
|
|
|
private resetBrowserHistory(): void {
|
|
this.document.defaultView?.history.replaceState(
|
|
{},
|
|
this.document.title,
|
|
this.document.defaultView.location.origin +
|
|
this.document.defaultView.location.pathname
|
|
);
|
|
}
|
|
|
|
private storeSigningKeys(
|
|
jwtKeys: JwtKeys,
|
|
config: OpenIdConfiguration
|
|
): void {
|
|
this.storagePersistenceService.write(JWT_KEYS, jwtKeys, config);
|
|
}
|
|
|
|
private readSigningKeys(config: OpenIdConfiguration): any {
|
|
return this.storagePersistenceService.read(JWT_KEYS, config);
|
|
}
|
|
}
|