angular-auth-oidc-client
Version:
An OpenID Connect Code Flow with PKCE,Implicit Flow client for Angular
1,207 lines • 149 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable, NgZone } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject, from, Observable, of, Subject, throwError as observableThrowError, timer } from 'rxjs';
import { catchError, filter, map, race, shareReplay, switchMap, switchMapTo, take, tap } from 'rxjs/operators';
import { OidcDataService } from '../data-services/oidc-data.service';
import { AuthorizationResult } from '../models/authorization-result';
import { AuthorizationState } from '../models/authorization-state.enum';
import { ValidateStateResult } from '../models/validate-state-result.model';
import { ValidationResult } from '../models/validation-result.enum';
import { ConfigurationProvider } from './auth-configuration.provider';
import { StateValidationService } from './oidc-security-state-validation.service';
import { TokenHelperService } from './oidc-token-helper.service';
import { LoggerService } from './oidc.logger.service';
import { OidcSecurityCheckSession } from './oidc.security.check-session';
import { OidcSecurityCommon } from './oidc.security.common';
import { OidcSecuritySilentRenew } from './oidc.security.silent-renew';
import { OidcSecurityUserService } from './oidc.security.user-service';
import { OidcSecurityValidation } from './oidc.security.validation';
import { UriEncoder } from './uri-encoder';
export class OidcSecurityService {
/**
* @param {?} oidcDataService
* @param {?} stateValidationService
* @param {?} router
* @param {?} oidcSecurityCheckSession
* @param {?} oidcSecuritySilentRenew
* @param {?} oidcSecurityUserService
* @param {?} oidcSecurityCommon
* @param {?} oidcSecurityValidation
* @param {?} tokenHelperService
* @param {?} loggerService
* @param {?} zone
* @param {?} httpClient
* @param {?} configurationProvider
*/
constructor(oidcDataService, stateValidationService, router, oidcSecurityCheckSession, oidcSecuritySilentRenew, oidcSecurityUserService, oidcSecurityCommon, oidcSecurityValidation, tokenHelperService, loggerService, zone, httpClient, configurationProvider) {
this.oidcDataService = oidcDataService;
this.stateValidationService = stateValidationService;
this.router = router;
this.oidcSecurityCheckSession = oidcSecurityCheckSession;
this.oidcSecuritySilentRenew = oidcSecuritySilentRenew;
this.oidcSecurityUserService = oidcSecurityUserService;
this.oidcSecurityCommon = oidcSecurityCommon;
this.oidcSecurityValidation = oidcSecurityValidation;
this.tokenHelperService = tokenHelperService;
this.loggerService = loggerService;
this.zone = zone;
this.httpClient = httpClient;
this.configurationProvider = configurationProvider;
this._onModuleSetup = new Subject();
this._onCheckSessionChanged = new Subject();
this._onAuthorizationResult = new Subject();
this.checkSessionChanged = false;
this.moduleSetup = false;
this._isModuleSetup = new BehaviorSubject(false);
this._isAuthorized = new BehaviorSubject(false);
this._userData = new BehaviorSubject('');
this.authWellKnownEndpointsLoaded = false;
this.runTokenValidationRunning = false;
this.onModuleSetup.pipe(take(1)).subscribe((/**
* @return {?}
*/
() => {
this.moduleSetup = true;
this._isModuleSetup.next(true);
}));
this._isSetupAndAuthorized = this._isModuleSetup.pipe(filter((/**
* @param {?} isModuleSetup
* @return {?}
*/
(isModuleSetup) => isModuleSetup)), switchMap((/**
* @return {?}
*/
() => {
if (!this.configurationProvider.openIDConfiguration.silent_renew) {
this.loggerService.logDebug(`IsAuthorizedRace: Silent Renew Not Active. Emitting.`);
return from([true]);
}
/** @type {?} */
const race$ = this._isAuthorized.asObservable().pipe(filter((/**
* @param {?} isAuthorized
* @return {?}
*/
(isAuthorized) => isAuthorized)), take(1), tap((/**
* @return {?}
*/
() => this.loggerService.logDebug('IsAuthorizedRace: Existing token is still authorized.'))), race(this._onAuthorizationResult.pipe(take(1), tap((/**
* @return {?}
*/
() => this.loggerService.logDebug('IsAuthorizedRace: Silent Renew Refresh Session Complete'))), map((/**
* @return {?}
*/
() => true))), timer(5000).pipe(
// backup, if nothing happens after 5 seconds stop waiting and emit
tap((/**
* @return {?}
*/
() => {
this.resetAuthorizationData(false);
this.oidcSecurityCommon.authNonce = '';
this.loggerService.logWarning('IsAuthorizedRace: Timeout reached. Emitting.');
})), map((/**
* @return {?}
*/
() => true)))));
this.loggerService.logDebug('Silent Renew is active, check if token in storage is active');
if (this.oidcSecurityCommon.authNonce === '' || this.oidcSecurityCommon.authNonce === undefined) {
// login not running, or a second silent renew, user must login first before this will work.
this.loggerService.logDebug('Silent Renew or login not running, try to refresh the session');
this.refreshSession();
}
return race$;
})), tap((/**
* @return {?}
*/
() => this.loggerService.logDebug('IsAuthorizedRace: Completed'))), switchMapTo(this._isAuthorized.asObservable()), tap((/**
* @param {?} isAuthorized
* @return {?}
*/
(isAuthorized) => this.loggerService.logDebug(`getIsAuthorized: ${isAuthorized}`))), shareReplay(1));
this._isSetupAndAuthorized
.pipe(filter((/**
* @return {?}
*/
() => this.configurationProvider.openIDConfiguration.start_checksession)))
.subscribe((/**
* @param {?} isSetupAndAuthorized
* @return {?}
*/
isSetupAndAuthorized => {
if (isSetupAndAuthorized) {
this.oidcSecurityCheckSession.startCheckingSession(this.configurationProvider.openIDConfiguration.client_id);
}
else {
this.oidcSecurityCheckSession.stopCheckingSession();
}
}));
}
/**
* @return {?}
*/
get onModuleSetup() {
return this._onModuleSetup.asObservable();
}
/**
* @return {?}
*/
get onAuthorizationResult() {
return this._onAuthorizationResult.asObservable();
}
/**
* @return {?}
*/
get onCheckSessionChanged() {
return this._onCheckSessionChanged.asObservable();
}
/**
* @return {?}
*/
get onConfigurationChange() {
return this.configurationProvider.onConfigurationChange;
}
/**
* @param {?} openIdConfiguration
* @param {?} authWellKnownEndpoints
* @return {?}
*/
setupModule(openIdConfiguration, authWellKnownEndpoints) {
this.configurationProvider.setup(openIdConfiguration, authWellKnownEndpoints);
this.oidcSecurityCheckSession.onCheckSessionChanged.subscribe((/**
* @return {?}
*/
() => {
this.loggerService.logDebug('onCheckSessionChanged');
this.checkSessionChanged = true;
this._onCheckSessionChanged.next(this.checkSessionChanged);
}));
/** @type {?} */
const userData = this.oidcSecurityCommon.userData;
if (userData) {
this.setUserData(userData);
}
/** @type {?} */
const isAuthorized = this.oidcSecurityCommon.isAuthorized;
if (isAuthorized) {
this.loggerService.logDebug('IsAuthorized setup module');
this.loggerService.logDebug(this.oidcSecurityCommon.idToken);
if (this.oidcSecurityValidation.isTokenExpired(this.oidcSecurityCommon.idToken, this.configurationProvider.openIDConfiguration.silent_renew_offset_in_seconds)) {
this.loggerService.logDebug('IsAuthorized setup module; id_token isTokenExpired');
}
else {
this.loggerService.logDebug('IsAuthorized setup module; id_token is valid');
this.setIsAuthorized(isAuthorized);
}
this.runTokenValidation();
}
this.loggerService.logDebug('STS server: ' + this.configurationProvider.openIDConfiguration.stsServer);
this._onModuleSetup.next();
if (this.configurationProvider.openIDConfiguration.silent_renew) {
this.oidcSecuritySilentRenew.initRenew();
// Support authorization via DOM events.
// Deregister if OidcSecurityService.setupModule is called again by any instance.
// We only ever want the latest setup service to be reacting to this event.
this.boundSilentRenewEvent = this.silentRenewEventHandler.bind(this);
/** @type {?} */
const instanceId = Math.random();
/** @type {?} */
const boundSilentRenewInitEvent = ((/**
* @param {?} e
* @return {?}
*/
(e) => {
if (e.detail !== instanceId) {
window.removeEventListener('oidc-silent-renew-message', this.boundSilentRenewEvent);
window.removeEventListener('oidc-silent-renew-init', boundSilentRenewInitEvent);
}
})).bind(this);
window.addEventListener('oidc-silent-renew-init', boundSilentRenewInitEvent, false);
window.addEventListener('oidc-silent-renew-message', this.boundSilentRenewEvent, false);
window.dispatchEvent(new CustomEvent('oidc-silent-renew-init', {
detail: instanceId,
}));
}
}
/**
* @return {?}
*/
getUserData() {
return this._userData.asObservable();
}
/**
* @return {?}
*/
getIsModuleSetup() {
return this._isModuleSetup.asObservable();
}
/**
* @return {?}
*/
getIsAuthorized() {
return this._isSetupAndAuthorized;
}
/**
* @return {?}
*/
getToken() {
if (!this._isAuthorized.getValue()) {
return '';
}
/** @type {?} */
const token = this.oidcSecurityCommon.getAccessToken();
return decodeURIComponent(token);
}
/**
* @return {?}
*/
getIdToken() {
if (!this._isAuthorized.getValue()) {
return '';
}
/** @type {?} */
const token = this.oidcSecurityCommon.getIdToken();
return decodeURIComponent(token);
}
/**
* @param {?=} encode
* @return {?}
*/
getPayloadFromIdToken(encode = false) {
/** @type {?} */
const token = this.getIdToken();
return this.tokenHelperService.getPayloadFromToken(token, encode);
}
/**
* @param {?} state
* @return {?}
*/
setState(state) {
this.oidcSecurityCommon.authStateControl = state;
}
/**
* @return {?}
*/
getState() {
return this.oidcSecurityCommon.authStateControl;
}
/**
* @param {?} params
* @return {?}
*/
setCustomRequestParameters(params) {
this.oidcSecurityCommon.customRequestParams = params;
}
// Code Flow with PCKE or Implicit Flow
/**
* @param {?=} urlHandler
* @return {?}
*/
authorize(urlHandler) {
if (this.configurationProvider.wellKnownEndpoints) {
this.authWellKnownEndpointsLoaded = true;
}
if (!this.authWellKnownEndpointsLoaded) {
this.loggerService.logError('Well known endpoints must be loaded before user can login!');
return;
}
if (!this.oidcSecurityValidation.config_validate_response_type(this.configurationProvider.openIDConfiguration.response_type)) {
// invalid response_type
return;
}
this.resetAuthorizationData(false);
this.loggerService.logDebug('BEGIN Authorize Code Flow, no auth data');
/** @type {?} */
let state = this.oidcSecurityCommon.authStateControl;
if (!state) {
state = Date.now() + '' + Math.random() + Math.random();
this.oidcSecurityCommon.authStateControl = state;
}
/** @type {?} */
const nonce = 'N' + Math.random() + '' + Date.now();
this.oidcSecurityCommon.authNonce = nonce;
this.loggerService.logDebug('AuthorizedController created. local state: ' + this.oidcSecurityCommon.authStateControl);
/** @type {?} */
let url = '';
// Code Flow
if (this.configurationProvider.openIDConfiguration.response_type === 'code') {
// code_challenge with "S256"
/** @type {?} */
const code_verifier = 'C' + Math.random() + '' + Date.now() + '' + Date.now() + Math.random();
/** @type {?} */
const code_challenge = this.oidcSecurityValidation.generate_code_verifier(code_verifier);
this.oidcSecurityCommon.code_verifier = code_verifier;
if (this.configurationProvider.wellKnownEndpoints) {
url = this.createAuthorizeUrl(true, code_challenge, this.configurationProvider.openIDConfiguration.redirect_url, nonce, state, this.configurationProvider.wellKnownEndpoints.authorization_endpoint || '');
}
else {
this.loggerService.logError('authWellKnownEndpoints is undefined');
}
}
else {
// Implicit Flow
if (this.configurationProvider.wellKnownEndpoints) {
url = this.createAuthorizeUrl(false, '', this.configurationProvider.openIDConfiguration.redirect_url, nonce, state, this.configurationProvider.wellKnownEndpoints.authorization_endpoint || '');
}
else {
this.loggerService.logError('authWellKnownEndpoints is undefined');
}
}
if (urlHandler) {
urlHandler(url);
}
else {
this.redirectTo(url);
}
}
// Code Flow
/**
* @param {?} urlToCheck
* @return {?}
*/
authorizedCallbackWithCode(urlToCheck) {
/** @type {?} */
const urlParts = urlToCheck.split('?');
/** @type {?} */
const params = new HttpParams({
fromString: urlParts[1],
});
/** @type {?} */
const code = params.get('code');
/** @type {?} */
const state = params.get('state');
/** @type {?} */
const session_state = params.get('session_state');
if (code && state) {
this.requestTokensWithCode(code, state, session_state);
}
}
// Code Flow
/**
* @param {?} code
* @param {?} state
* @param {?} session_state
* @return {?}
*/
requestTokensWithCode(code, state, session_state) {
this._isModuleSetup
.pipe(filter((/**
* @param {?} isModuleSetup
* @return {?}
*/
(isModuleSetup) => isModuleSetup)), take(1))
.subscribe((/**
* @return {?}
*/
() => {
this.requestTokensWithCodeProcedure(code, state, session_state);
}));
}
// Code Flow with PCKE
/**
* @param {?} code
* @param {?} state
* @param {?} session_state
* @return {?}
*/
requestTokensWithCodeProcedure(code, state, session_state) {
/** @type {?} */
let tokenRequestUrl = '';
if (this.configurationProvider.wellKnownEndpoints && this.configurationProvider.wellKnownEndpoints.token_endpoint) {
tokenRequestUrl = `${this.configurationProvider.wellKnownEndpoints.token_endpoint}`;
}
if (!this.oidcSecurityValidation.validateStateFromHashCallback(state, this.oidcSecurityCommon.authStateControl)) {
this.loggerService.logWarning('authorizedCallback incorrect state');
// ValidationResult.StatesDoNotMatch;
return;
}
/** @type {?} */
let headers = new HttpHeaders();
headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
/** @type {?} */
let data = `grant_type=authorization_code&client_id=${this.configurationProvider.openIDConfiguration.client_id}` +
`&code_verifier=${this.oidcSecurityCommon.code_verifier}&code=${code}&redirect_uri=${this.configurationProvider.openIDConfiguration.redirect_url}`;
if (this.oidcSecurityCommon.silentRenewRunning === 'running') {
data =
`grant_type=authorization_code&client_id=${this.configurationProvider.openIDConfiguration.client_id}` +
`&code_verifier=${this.oidcSecurityCommon.code_verifier}&code=${code}&redirect_uri=${this.configurationProvider.openIDConfiguration.silent_renew_url}`;
}
this.httpClient
.post(tokenRequestUrl, data, { headers: headers })
.pipe(map((/**
* @param {?} response
* @return {?}
*/
response => {
/** @type {?} */
let obj = new Object();
obj = response;
obj.state = state;
obj.session_state = session_state;
this.authorizedCodeFlowCallbackProcedure(obj);
})), catchError((/**
* @param {?} error
* @return {?}
*/
error => {
this.loggerService.logError(error);
this.loggerService.logError(`OidcService code request ${this.configurationProvider.openIDConfiguration.stsServer}`);
return of(false);
})))
.subscribe();
}
// Code Flow
/**
* @private
* @param {?} result
* @return {?}
*/
authorizedCodeFlowCallbackProcedure(result) {
/** @type {?} */
const silentRenew = this.oidcSecurityCommon.silentRenewRunning;
/** @type {?} */
const isRenewProcess = silentRenew === 'running';
this.loggerService.logDebug('BEGIN authorized Code Flow Callback, no auth data');
this.resetAuthorizationData(isRenewProcess);
this.authorizedCallbackProcedure(result, isRenewProcess);
}
// Implicit Flow
/**
* @private
* @param {?=} hash
* @return {?}
*/
authorizedImplicitFlowCallbackProcedure(hash) {
/** @type {?} */
const silentRenew = this.oidcSecurityCommon.silentRenewRunning;
/** @type {?} */
const isRenewProcess = silentRenew === 'running';
this.loggerService.logDebug('BEGIN authorizedCallback, no auth data');
this.resetAuthorizationData(isRenewProcess);
hash = hash || window.location.hash.substr(1);
/** @type {?} */
const result = hash.split('&').reduce((/**
* @param {?} resultData
* @param {?} item
* @return {?}
*/
function (resultData, item) {
/** @type {?} */
const parts = item.split('=');
resultData[(/** @type {?} */ (parts.shift()))] = parts.join('=');
return resultData;
}), {});
this.authorizedCallbackProcedure(result, isRenewProcess);
}
// Implicit Flow
/**
* @param {?=} hash
* @return {?}
*/
authorizedImplicitFlowCallback(hash) {
this._isModuleSetup
.pipe(filter((/**
* @param {?} isModuleSetup
* @return {?}
*/
(isModuleSetup) => isModuleSetup)), take(1))
.subscribe((/**
* @return {?}
*/
() => {
this.authorizedImplicitFlowCallbackProcedure(hash);
}));
}
/**
* @private
* @param {?} url
* @return {?}
*/
redirectTo(url) {
window.location.href = url;
}
// Implicit Flow
/**
* @private
* @param {?} result
* @param {?} isRenewProcess
* @return {?}
*/
authorizedCallbackProcedure(result, isRenewProcess) {
this.oidcSecurityCommon.authResult = result;
if (!this.configurationProvider.openIDConfiguration.history_cleanup_off && !isRenewProcess) {
// reset the history to remove the tokens
window.history.replaceState({}, window.document.title, window.location.origin + window.location.pathname);
}
else {
this.loggerService.logDebug('history clean up inactive');
}
if (result.error) {
if (isRenewProcess) {
this.loggerService.logDebug(result);
}
else {
this.loggerService.logWarning(result);
}
if (((/** @type {?} */ (result.error))) === 'login_required') {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, ValidationResult.LoginRequired));
}
else {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, ValidationResult.SecureTokenServerError));
}
this.resetAuthorizationData(false);
this.oidcSecurityCommon.authNonce = '';
if (!this.configurationProvider.openIDConfiguration.trigger_authorization_result_event && !isRenewProcess) {
this.router.navigate([this.configurationProvider.openIDConfiguration.unauthorized_route]);
}
}
else {
this.loggerService.logDebug(result);
this.loggerService.logDebug('authorizedCallback created, begin token validation');
this.getSigningKeys().subscribe((/**
* @param {?} jwtKeys
* @return {?}
*/
jwtKeys => {
/** @type {?} */
const validationResult = this.getValidatedStateResult(result, jwtKeys);
if (validationResult.authResponseIsValid) {
this.setAuthorizationData(validationResult.access_token, validationResult.id_token);
this.oidcSecurityCommon.silentRenewRunning = '';
if (this.configurationProvider.openIDConfiguration.auto_userinfo) {
this.getUserinfo(isRenewProcess, result, validationResult.id_token, validationResult.decoded_id_token).subscribe((/**
* @param {?} response
* @return {?}
*/
response => {
if (response) {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.authorized, validationResult.state));
if (!this.configurationProvider.openIDConfiguration.trigger_authorization_result_event && !isRenewProcess) {
this.router.navigate([this.configurationProvider.openIDConfiguration.post_login_route]);
}
}
else {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, validationResult.state));
if (!this.configurationProvider.openIDConfiguration.trigger_authorization_result_event && !isRenewProcess) {
this.router.navigate([this.configurationProvider.openIDConfiguration.unauthorized_route]);
}
}
}), (/**
* @param {?} err
* @return {?}
*/
err => {
/* Something went wrong while getting signing key */
this.loggerService.logWarning('Failed to retreive user info with error: ' + JSON.stringify(err));
}));
}
else {
if (!isRenewProcess) {
// userData is set to the id_token decoded, auto get user data set to false
this.oidcSecurityUserService.setUserData(validationResult.decoded_id_token);
this.setUserData(this.oidcSecurityUserService.getUserData());
}
this.runTokenValidation();
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.authorized, validationResult.state));
if (!this.configurationProvider.openIDConfiguration.trigger_authorization_result_event && !isRenewProcess) {
this.router.navigate([this.configurationProvider.openIDConfiguration.post_login_route]);
}
}
}
else {
// something went wrong
this.loggerService.logWarning('authorizedCallback, token(s) validation failed, resetting');
this.loggerService.logWarning(window.location.hash);
this.resetAuthorizationData(false);
this.oidcSecurityCommon.silentRenewRunning = '';
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, validationResult.state));
if (!this.configurationProvider.openIDConfiguration.trigger_authorization_result_event && !isRenewProcess) {
this.router.navigate([this.configurationProvider.openIDConfiguration.unauthorized_route]);
}
}
}), (/**
* @param {?} err
* @return {?}
*/
err => {
/* Something went wrong while getting signing key */
this.loggerService.logWarning('Failed to retreive siging key with error: ' + JSON.stringify(err));
this.oidcSecurityCommon.silentRenewRunning = '';
}));
}
}
/**
* @param {?=} isRenewProcess
* @param {?=} result
* @param {?=} id_token
* @param {?=} decoded_id_token
* @return {?}
*/
getUserinfo(isRenewProcess = false, result, id_token, decoded_id_token) {
result = result ? result : this.oidcSecurityCommon.authResult;
id_token = id_token ? id_token : this.oidcSecurityCommon.idToken;
decoded_id_token = decoded_id_token ? decoded_id_token : this.tokenHelperService.getPayloadFromToken(id_token, false);
return new Observable((/**
* @param {?} observer
* @return {?}
*/
observer => {
// flow id_token token
if (this.configurationProvider.openIDConfiguration.response_type === 'id_token token' ||
this.configurationProvider.openIDConfiguration.response_type === 'code') {
if (isRenewProcess && this._userData.value) {
this.oidcSecurityCommon.sessionState = result.session_state;
observer.next(true);
observer.complete();
}
else {
this.oidcSecurityUserService.initUserData().subscribe((/**
* @return {?}
*/
() => {
this.loggerService.logDebug('authorizedCallback (id_token token || code) flow');
/** @type {?} */
const userData = this.oidcSecurityUserService.getUserData();
if (this.oidcSecurityValidation.validate_userdata_sub_id_token(decoded_id_token.sub, userData.sub)) {
this.setUserData(userData);
this.loggerService.logDebug(this.oidcSecurityCommon.accessToken);
this.loggerService.logDebug(this.oidcSecurityUserService.getUserData());
this.oidcSecurityCommon.sessionState = result.session_state;
this.runTokenValidation();
observer.next(true);
}
else {
// something went wrong, userdata sub does not match that from id_token
this.loggerService.logWarning('authorizedCallback, User data sub does not match sub in id_token');
this.loggerService.logDebug('authorizedCallback, token(s) validation failed, resetting');
this.resetAuthorizationData(false);
observer.next(false);
}
observer.complete();
}));
}
}
else {
// flow id_token
this.loggerService.logDebug('authorizedCallback id_token flow');
this.loggerService.logDebug(this.oidcSecurityCommon.accessToken);
// userData is set to the id_token decoded. No access_token.
this.oidcSecurityUserService.setUserData(decoded_id_token);
this.setUserData(this.oidcSecurityUserService.getUserData());
this.oidcSecurityCommon.sessionState = result.session_state;
this.runTokenValidation();
observer.next(true);
observer.complete();
}
}));
}
/**
* @param {?=} urlHandler
* @return {?}
*/
logoff(urlHandler) {
// /connect/endsession?id_token_hint=...&post_logout_redirect_uri=https://myapp.com
this.loggerService.logDebug('BEGIN Authorize, no auth data');
if (this.configurationProvider.wellKnownEndpoints) {
if (this.configurationProvider.wellKnownEndpoints.end_session_endpoint) {
/** @type {?} */
const end_session_endpoint = this.configurationProvider.wellKnownEndpoints.end_session_endpoint;
/** @type {?} */
const id_token_hint = this.oidcSecurityCommon.idToken;
/** @type {?} */
const url = this.createEndSessionUrl(end_session_endpoint, id_token_hint);
this.resetAuthorizationData(false);
if (this.configurationProvider.openIDConfiguration.start_checksession && this.checkSessionChanged) {
this.loggerService.logDebug('only local login cleaned up, server session has changed');
}
else if (urlHandler) {
urlHandler(url);
}
else {
this.redirectTo(url);
}
}
else {
this.resetAuthorizationData(false);
this.loggerService.logDebug('only local login cleaned up, no end_session_endpoint');
}
}
else {
this.loggerService.logWarning('authWellKnownEndpoints is undefined');
}
}
/**
* @return {?}
*/
refreshSession() {
if (!this.configurationProvider.openIDConfiguration.silent_renew) {
return from([false]);
}
this.loggerService.logDebug('BEGIN refresh session Authorize');
/** @type {?} */
let state = this.oidcSecurityCommon.authStateControl;
if (state === '' || state === null) {
state = Date.now() + '' + Math.random() + Math.random();
this.oidcSecurityCommon.authStateControl = state;
}
/** @type {?} */
const nonce = 'N' + Math.random() + '' + Date.now();
this.oidcSecurityCommon.authNonce = nonce;
this.loggerService.logDebug('RefreshSession created. adding myautostate: ' + this.oidcSecurityCommon.authStateControl);
/** @type {?} */
let url = '';
// Code Flow
if (this.configurationProvider.openIDConfiguration.response_type === 'code') {
// code_challenge with "S256"
/** @type {?} */
const code_verifier = 'C' + Math.random() + '' + Date.now() + '' + Date.now() + Math.random();
/** @type {?} */
const code_challenge = this.oidcSecurityValidation.generate_code_verifier(code_verifier);
this.oidcSecurityCommon.code_verifier = code_verifier;
if (this.configurationProvider.wellKnownEndpoints) {
url = this.createAuthorizeUrl(true, code_challenge, this.configurationProvider.openIDConfiguration.silent_renew_url, nonce, state, this.configurationProvider.wellKnownEndpoints.authorization_endpoint || '', 'none');
}
else {
this.loggerService.logWarning('authWellKnownEndpoints is undefined');
}
}
else {
if (this.configurationProvider.wellKnownEndpoints) {
url = this.createAuthorizeUrl(false, '', this.configurationProvider.openIDConfiguration.silent_renew_url, nonce, state, this.configurationProvider.wellKnownEndpoints.authorization_endpoint || '', 'none');
}
else {
this.loggerService.logWarning('authWellKnownEndpoints is undefined');
}
}
this.oidcSecurityCommon.silentRenewRunning = 'running';
return this.oidcSecuritySilentRenew.startRenew(url);
}
/**
* @param {?} error
* @return {?}
*/
handleError(error) {
this.loggerService.logError(error);
if (error.status === 403 || error.status === '403') {
if (this.configurationProvider.openIDConfiguration.trigger_authorization_result_event) {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, ValidationResult.NotSet));
}
else {
this.router.navigate([this.configurationProvider.openIDConfiguration.forbidden_route]);
}
}
else if (error.status === 401 || error.status === '401') {
/** @type {?} */
const silentRenew = this.oidcSecurityCommon.silentRenewRunning;
this.resetAuthorizationData(!!silentRenew);
if (this.configurationProvider.openIDConfiguration.trigger_authorization_result_event) {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, ValidationResult.NotSet));
}
else {
this.router.navigate([this.configurationProvider.openIDConfiguration.unauthorized_route]);
}
}
}
/**
* @return {?}
*/
startCheckingSilentRenew() {
this.runTokenValidation();
}
/**
* @return {?}
*/
stopCheckingSilentRenew() {
if (this._scheduledHeartBeat) {
clearTimeout(this._scheduledHeartBeat);
this._scheduledHeartBeat = null;
this.runTokenValidationRunning = false;
}
}
/**
* @param {?} isRenewProcess
* @return {?}
*/
resetAuthorizationData(isRenewProcess) {
if (!isRenewProcess) {
if (this.configurationProvider.openIDConfiguration.auto_userinfo) {
// Clear user data. Fixes #97.
this.setUserData('');
}
this.oidcSecurityCommon.resetStorageData(isRenewProcess);
this.checkSessionChanged = false;
this.setIsAuthorized(false);
}
}
/**
* @return {?}
*/
getEndSessionUrl() {
if (this.configurationProvider.wellKnownEndpoints) {
if (this.configurationProvider.wellKnownEndpoints.end_session_endpoint) {
/** @type {?} */
const end_session_endpoint = this.configurationProvider.wellKnownEndpoints.end_session_endpoint;
/** @type {?} */
const id_token_hint = this.oidcSecurityCommon.idToken;
return this.createEndSessionUrl(end_session_endpoint, id_token_hint);
}
}
}
/**
* @private
* @param {?} result
* @param {?} jwtKeys
* @return {?}
*/
getValidatedStateResult(result, jwtKeys) {
if (result.error) {
return new ValidateStateResult('', '', false, {});
}
return this.stateValidationService.validateState(result, jwtKeys);
}
/**
* @private
* @param {?} userData
* @return {?}
*/
setUserData(userData) {
this.oidcSecurityCommon.userData = userData;
this._userData.next(userData);
}
/**
* @private
* @param {?} isAuthorized
* @return {?}
*/
setIsAuthorized(isAuthorized) {
this._isAuthorized.next(isAuthorized);
}
/**
* @private
* @param {?} access_token
* @param {?} id_token
* @return {?}
*/
setAuthorizationData(access_token, id_token) {
if (this.oidcSecurityCommon.accessToken !== '') {
this.oidcSecurityCommon.accessToken = '';
}
this.loggerService.logDebug(access_token);
this.loggerService.logDebug(id_token);
this.loggerService.logDebug('storing to storage, getting the roles');
this.oidcSecurityCommon.accessToken = access_token;
this.oidcSecurityCommon.idToken = id_token;
this.setIsAuthorized(true);
this.oidcSecurityCommon.isAuthorized = true;
}
/**
* @private
* @param {?} isCodeFlow
* @param {?} code_challenge
* @param {?} redirect_url
* @param {?} nonce
* @param {?} state
* @param {?} authorization_endpoint
* @param {?=} prompt
* @return {?}
*/
createAuthorizeUrl(isCodeFlow, code_challenge, redirect_url, nonce, state, authorization_endpoint, prompt) {
/** @type {?} */
const urlParts = authorization_endpoint.split('?');
/** @type {?} */
const authorizationUrl = urlParts[0];
/** @type {?} */
let params = new HttpParams({
fromString: urlParts[1],
encoder: new UriEncoder(),
});
params = params.set('client_id', this.configurationProvider.openIDConfiguration.client_id);
params = params.append('redirect_uri', redirect_url);
params = params.append('response_type', this.configurationProvider.openIDConfiguration.response_type);
params = params.append('scope', this.configurationProvider.openIDConfiguration.scope);
params = params.append('nonce', nonce);
params = params.append('state', state);
if (isCodeFlow) {
params = params.append('code_challenge', code_challenge);
params = params.append('code_challenge_method', 'S256');
}
if (prompt) {
params = params.append('prompt', prompt);
}
if (this.configurationProvider.openIDConfiguration.hd_param) {
params = params.append('hd', this.configurationProvider.openIDConfiguration.hd_param);
}
/** @type {?} */
const customParams = Object.assign({}, this.oidcSecurityCommon.customRequestParams);
Object.keys(customParams).forEach((/**
* @param {?} key
* @return {?}
*/
key => {
params = params.append(key, customParams[key].toString());
}));
return `${authorizationUrl}?${params}`;
}
/**
* @private
* @param {?} end_session_endpoint
* @param {?} id_token_hint
* @return {?}
*/
createEndSessionUrl(end_session_endpoint, id_token_hint) {
/** @type {?} */
const urlParts = end_session_endpoint.split('?');
/** @type {?} */
const authorizationEndsessionUrl = urlParts[0];
/** @type {?} */
let params = new HttpParams({
fromString: urlParts[1],
encoder: new UriEncoder(),
});
params = params.set('id_token_hint', id_token_hint);
params = params.append('post_logout_redirect_uri', this.configurationProvider.openIDConfiguration.post_logout_redirect_uri);
return `${authorizationEndsessionUrl}?${params}`;
}
/**
* @private
* @return {?}
*/
getSigningKeys() {
if (this.configurationProvider.wellKnownEndpoints) {
this.loggerService.logDebug('jwks_uri: ' + this.configurationProvider.wellKnownEndpoints.jwks_uri);
return this.oidcDataService
.get(this.configurationProvider.wellKnownEndpoints.jwks_uri || '')
.pipe(catchError(this.handleErrorGetSigningKeys));
}
else {
this.loggerService.logWarning('getSigningKeys: authWellKnownEndpoints is undefined');
}
return this.oidcDataService.get('undefined').pipe(catchError(this.handleErrorGetSigningKeys));
}
/**
* @private
* @param {?} error
* @return {?}
*/
handleErrorGetSigningKeys(error) {
/** @type {?} */
let errMsg;
if (error instanceof Response) {
/** @type {?} */
const body = error.json() || {};
/** @type {?} */
const err = JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
}
else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return observableThrowError(errMsg);
}
/**
* @private
* @return {?}
*/
runTokenValidation() {
if (this.runTokenValidationRunning || !this.configurationProvider.openIDConfiguration.silent_renew) {
return;
}
this.runTokenValidationRunning = true;
this.loggerService.logDebug('runTokenValidation silent-renew running');
/**
* First time: delay 10 seconds to call silentRenewHeartBeatCheck
* Afterwards: Run this check in a 5 second interval only AFTER the previous operation ends.
* @type {?}
*/
const silentRenewHeartBeatCheck = (/**
* @return {?}
*/
() => {
this.loggerService.logDebug('silentRenewHeartBeatCheck\r\n' +
`\tsilentRenewRunning: ${this.oidcSecurityCommon.silentRenewRunning === 'running'}\r\n` +
`\tidToken: ${!!this.getIdToken()}\r\n` +
`\t_userData.value: ${!!this._userData.value}`);
if (this._userData.value && this.oidcSecurityCommon.silentRenewRunning !== 'running' && this.getIdToken()) {
if (this.oidcSecurityValidation.isTokenExpired(this.oidcSecurityCommon.idToken, this.configurationProvider.openIDConfiguration.silent_renew_offset_in_seconds)) {
this.loggerService.logDebug('IsAuthorized: id_token isTokenExpired, start silent renew if active');
if (this.configurationProvider.openIDConfiguration.silent_renew) {
this.refreshSession().subscribe((/**
* @return {?}
*/
() => {
this._scheduledHeartBeat = setTimeout(silentRenewHeartBeatCheck, 3000);
}), (/**
* @param {?} err
* @return {?}
*/
(err) => {
this.loggerService.logError('Error: ' + err);
this._scheduledHeartBeat = setTimeout(silentRenewHeartBeatCheck, 3000);
}));
/* In this situation, we schedule a heatbeat check only when silentRenew is finished.
We don't want to schedule another check so we have to return here */
return;
}
else {
this.resetAuthorizationData(false);
}
}
}
/* Delay 3 seconds and do the next check */
this._scheduledHeartBeat = setTimeout(silentRenewHeartBeatCheck, 3000);
});
this.zone.runOutsideAngular((/**
* @return {?}
*/
() => {
/* Initial heartbeat check */
this._scheduledHeartBeat = setTimeout(silentRenewHeartBeatCheck, 10000);
}));
}
/**
* @private
* @param {?} e
* @return {?}
*/
silentRenewEventHandler(e) {
this.loggerService.logDebug('silentRenewEventHandler');
if (this.configurationProvider.openIDConfiguration.response_type === 'code') {
/** @type {?} */
const urlParts = e.detail.toString().split('?');
/** @type {?} */
const params = new HttpParams({
fromString: urlParts[1],
});
/** @type {?} */
const code = params.get('code');
/** @type {?} */
const state = params.get('state');
/** @type {?} */
const session_state = params.get('session_state');
/** @type {?} */
const error = params.get('error');
if (code && state) {
this.requestTokensWithCodeProcedure(code, state, session_state);
}
if (error) {
this._onAuthorizationResult.next(new AuthorizationResult(AuthorizationState.unauthorized, ValidationResult.LoginRequired));
this.resetAuthorizationData(false);
this.oidcSecurityCommon.authNonce = '';
this.loggerService.logDebug(e.detail.toString());
}
}
else {
// ImplicitFlow
this.authorizedImplicitFlowCallback(e.detail);
}
}
}
OidcSecurityService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
OidcSecurityService.ctorParameters = () => [
{ type: OidcDataService },
{ type: StateValidationService },
{ type: Router },
{ type: OidcSecurityCheckSession },
{ type: OidcSecuritySilentRenew },
{ type: OidcSecurityUserService },
{ type: OidcSecurityCommon },
{ type: OidcSecurityValidation },
{ type: TokenHelperService },
{ type: LoggerService },
{ type: NgZone },
{ type: HttpClient },
{ type: ConfigurationProvider }
];
if (false) {
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._onModuleSetup;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._onCheckSessionChanged;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._onAuthorizationResult;
/** @type {?} */
OidcSecurityService.prototype.checkSessionChanged;
/** @type {?} */
OidcSecurityService.prototype.moduleSetup;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._isModuleSetup;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._isAuthorized;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._isSetupAndAuthorized;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._userData;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.authWellKnownEndpointsLoaded;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.runTokenValidationRunning;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype._scheduledHeartBeat;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.boundSilentRenewEvent;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.oidcDataService;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.stateValidationService;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.router;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.oidcSecurityCheckSession;
/**
* @type {?}
* @private
*/
OidcSecurityService.prototype.oidcSe