UNPKG

angular-oauth2-oidc-codeflow-pkce

Version:

[![Build Status](https://travis-ci.org/bechhansen/angular-oauth2-oidc.svg?branch=master)](https://travis-ci.org/bechhansen/angular-oauth2-oidc)

1,188 lines (1,187 loc) 74 kB
import { __extends, __values, __spread } from 'tslib'; import { Injectable, NgZone, Optional, NgModule, InjectionToken } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams, HTTP_INTERCEPTORS } from '@angular/common/http'; import { Subject, of, race, throwError } from 'rxjs'; import { filter, delay, first, tap, map, catchError } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; import { KEYUTIL, KJUR } from 'jsrsasign'; var LoginOptions = /** @class */ (function () { function LoginOptions() { this.preventClearHashAfterLogin = false; } return LoginOptions; }()); var OAuthStorage = /** @class */ (function () { function OAuthStorage() { } return OAuthStorage; }()); var ReceivedTokens = /** @class */ (function () { function ReceivedTokens() { } return ReceivedTokens; }()); var ValidationHandler = /** @class */ (function () { function ValidationHandler() { } return ValidationHandler; }()); var AbstractValidationHandler = /** @class */ (function () { function AbstractValidationHandler() { } AbstractValidationHandler.prototype.validateAtHash = function (params) { var hashAlg = this.inferHashAlgorithm(params.idTokenHeader); var tokenHash = this.calcHash(params.accessToken, hashAlg); var leftMostHalf = tokenHash.substr(0, tokenHash.length / 2); var tokenHashBase64 = btoa(leftMostHalf); var atHash = tokenHashBase64 .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); var claimsAtHash = params.idTokenClaims['at_hash'].replace(/=/g, ''); if (atHash !== claimsAtHash) { console.error('exptected at_hash: ' + atHash); console.error('actual at_hash: ' + claimsAtHash); } return atHash === claimsAtHash; }; AbstractValidationHandler.prototype.inferHashAlgorithm = function (jwtHeader) { var alg = jwtHeader['alg']; if (!alg.match(/^.S[0-9]{3}$/)) { throw new Error('Algorithm not supported: ' + alg); } return 'sha' + alg.substr(2); }; return AbstractValidationHandler; }()); var UrlHelperService = /** @class */ (function () { function UrlHelperService() { } UrlHelperService.prototype.getHashFragmentParams = function (customHashFragment) { var hash = customHashFragment || window.location.hash; hash = decodeURIComponent(hash); if (hash.indexOf('#') !== 0) { return {}; } var questionMarkPosition = hash.indexOf('?'); if (questionMarkPosition > -1) { hash = hash.substr(questionMarkPosition + 1); } else { hash = hash.substr(1); } return this.parseQueryString(hash); }; UrlHelperService.prototype.parseQueryString = function (queryString) { var data = {}; var pairs, pair, separatorIndex, escapedKey, escapedValue, key, value; if (queryString === null) { return data; } pairs = queryString.split('&'); for (var i = 0; i < pairs.length; i++) { pair = pairs[i]; separatorIndex = pair.indexOf('='); if (separatorIndex === -1) { escapedKey = pair; escapedValue = null; } else { escapedKey = pair.substr(0, separatorIndex); escapedValue = pair.substr(separatorIndex + 1); } key = decodeURIComponent(escapedKey); value = decodeURIComponent(escapedValue); if (key.substr(0, 1) === '/') { key = key.substr(1); } data[key] = value; } return data; }; return UrlHelperService; }()); UrlHelperService.decorators = [ { type: Injectable }, ]; var OAuthEvent = /** @class */ (function () { function OAuthEvent(type) { this.type = type; } return OAuthEvent; }()); var OAuthSuccessEvent = /** @class */ (function (_super) { __extends(OAuthSuccessEvent, _super); function OAuthSuccessEvent(type, info) { if (info === void 0) { info = null; } var _this = _super.call(this, type) || this; _this.info = info; return _this; } return OAuthSuccessEvent; }(OAuthEvent)); var OAuthInfoEvent = /** @class */ (function (_super) { __extends(OAuthInfoEvent, _super); function OAuthInfoEvent(type, info) { if (info === void 0) { info = null; } var _this = _super.call(this, type) || this; _this.info = info; return _this; } return OAuthInfoEvent; }(OAuthEvent)); var OAuthErrorEvent = /** @class */ (function (_super) { __extends(OAuthErrorEvent, _super); function OAuthErrorEvent(type, reason, params) { if (params === void 0) { params = null; } var _this = _super.call(this, type) || this; _this.reason = reason; _this.params = params; return _this; } return OAuthErrorEvent; }(OAuthEvent)); function b64DecodeUnicode(str) { var base64 = str.replace(/\-/g, '+').replace(/\_/g, '/'); return decodeURIComponent(atob(base64) .split('') .map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }) .join('')); } var AuthConfig = /** @class */ (function () { function AuthConfig(json) { this.clientId = ''; this.redirectUri = ''; this.postLogoutRedirectUri = ''; this.loginUrl = ''; this.scope = 'openid profile'; this.resource = ''; this.rngUrl = ''; this.oidc = true; this.requestAccessToken = true; this.options = null; this.issuer = ''; this.logoutUrl = ''; this.clearHashAfterLogin = true; this.tokenEndpoint = null; this.customUserinfoEndpoint = null; this.userinfoEndpoint = null; this.responseType = 'token'; this.showDebugInformation = false; this.silentRefreshRedirectUri = ''; this.silentRefreshMessagePrefix = ''; this.silentRefreshShowIFrame = false; this.siletRefreshTimeout = 1000 * 20; this.silentRefreshTimeout = 1000 * 20; this.dummyClientSecret = null; this.requireHttps = 'remoteOnly'; this.strictDiscoveryDocumentValidation = true; this.jwks = null; this.customQueryParams = null; this.silentRefreshIFrameName = 'angular-oauth-oidc-silent-refresh-iframe'; this.timeoutFactor = 0.75; this.sessionChecksEnabled = false; this.sessionCheckIntervall = 3 * 1000; this.sessionCheckIFrameUrl = null; this.sessionCheckIFrameName = 'angular-oauth-oidc-check-session-iframe'; this.disableAtHashCheck = false; this.skipSubjectCheck = false; this.useIdTokenHintForSilentRefresh = false; this.skipIssuerCheck = false; this.nonceStateSeparator = ';'; this.useHttpBasicAuthForPasswordFlow = false; this.disableNonceCheck = false; this.openUri = function (uri) { location.href = uri; }; if (json) { Object.assign(this, json); } } return AuthConfig; }()); var WebHttpUrlEncodingCodec = /** @class */ (function () { function WebHttpUrlEncodingCodec() { } WebHttpUrlEncodingCodec.prototype.encodeKey = function (k) { return encodeURIComponent(k); }; WebHttpUrlEncodingCodec.prototype.encodeValue = function (v) { return encodeURIComponent(v); }; WebHttpUrlEncodingCodec.prototype.decodeKey = function (k) { return decodeURIComponent(k); }; WebHttpUrlEncodingCodec.prototype.decodeValue = function (v) { return decodeURIComponent(v); }; return WebHttpUrlEncodingCodec; }()); var OAuthService = /** @class */ (function (_super) { __extends(OAuthService, _super); function OAuthService(ngZone, http, storage, tokenValidationHandler, config, urlHelper) { var _this = _super.call(this) || this; _this.ngZone = ngZone; _this.http = http; _this.config = config; _this.urlHelper = urlHelper; _this.discoveryDocumentLoaded = false; _this.state = ''; _this.eventsSubject = new Subject(); _this.discoveryDocumentLoadedSubject = new Subject(); _this.grantTypesSupported = []; _this.inImplicitFlow = false; _this.discoveryDocumentLoaded$ = _this.discoveryDocumentLoadedSubject.asObservable(); _this.events = _this.eventsSubject.asObservable(); if (tokenValidationHandler) { _this.tokenValidationHandler = tokenValidationHandler; } if (config) { _this.configure(config); } try { if (storage) { _this.setStorage(storage); } else if (typeof sessionStorage !== 'undefined') { _this.setStorage(sessionStorage); } } catch (e) { console.error('cannot access sessionStorage. Consider setting an own storage implementation using setStorage', e); } _this.setupRefreshTimer(); return _this; } OAuthService.prototype.configure = function (config) { Object.assign(this, new AuthConfig(), config); this.config = Object.assign(({}), new AuthConfig(), config); if (this.sessionChecksEnabled) { this.setupSessionCheck(); } this.configChanged(); }; OAuthService.prototype.configChanged = function () { }; OAuthService.prototype.restartSessionChecksIfStillLoggedIn = function () { if (this.hasValidIdToken()) { this.initSessionCheck(); } }; OAuthService.prototype.restartRefreshTimerIfStillLoggedIn = function () { this.setupExpirationTimers(); }; OAuthService.prototype.setupSessionCheck = function () { var _this = this; this.events.pipe(filter(function (e) { return e.type === 'token_received'; })).subscribe(function (e) { _this.initSessionCheck(); }); }; OAuthService.prototype.setupAutomaticSilentRefresh = function (params) { var _this = this; if (params === void 0) { params = {}; } this.events.pipe(filter(function (e) { return e.type === 'token_expires'; })).subscribe(function (e) { _this.silentRefresh(params).catch(function (_) { _this.debug('automatic silent refresh did not work'); }); }); this.restartRefreshTimerIfStillLoggedIn(); }; OAuthService.prototype.loadDiscoveryDocumentAndTryLogin = function (options) { var _this = this; if (options === void 0) { options = null; } return this.loadDiscoveryDocument().then(function (doc) { return _this.tryLogin(options); }); }; OAuthService.prototype.loadDiscoveryDocumentAndLogin = function (options) { var _this = this; if (options === void 0) { options = null; } return this.loadDiscoveryDocumentAndTryLogin(options).then(function (_) { if (!_this.hasValidIdToken() || !_this.hasValidAccessToken()) { _this.initImplicitFlow(); return false; } else { return true; } }); }; OAuthService.prototype.debug = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.showDebugInformation) { console.debug.apply(console, args); } }; OAuthService.prototype.validateUrlFromDiscoveryDocument = function (url) { var errors = []; var httpsCheck = this.validateUrlForHttps(url); var issuerCheck = this.validateUrlAgainstIssuer(url); if (!httpsCheck) { errors.push('https for all urls required. Also for urls received by discovery.'); } if (!issuerCheck) { errors.push('Every url in discovery document has to start with the issuer url.' + 'Also see property strictDiscoveryDocumentValidation.'); } return errors; }; OAuthService.prototype.validateUrlForHttps = function (url) { if (!url) { return true; } var lcUrl = url.toLowerCase(); if (this.requireHttps === false) { return true; } if ((lcUrl.match(/^http:\/\/localhost($|[:\/])/) || lcUrl.match(/^http:\/\/localhost($|[:\/])/)) && this.requireHttps === 'remoteOnly') { return true; } return lcUrl.startsWith('https://'); }; OAuthService.prototype.validateUrlAgainstIssuer = function (url) { if (!this.strictDiscoveryDocumentValidation) { return true; } if (!url) { return true; } return url.toLowerCase().startsWith(this.issuer.toLowerCase()); }; OAuthService.prototype.setupRefreshTimer = function () { var _this = this; if (typeof window === 'undefined') { this.debug('timer not supported on this plattform'); return; } if (this.hasValidIdToken()) { this.clearAccessTokenTimer(); this.clearIdTokenTimer(); this.setupExpirationTimers(); } this.events.pipe(filter(function (e) { return e.type === 'token_received'; })).subscribe(function (_) { _this.clearAccessTokenTimer(); _this.clearIdTokenTimer(); _this.setupExpirationTimers(); }); }; OAuthService.prototype.setupExpirationTimers = function () { var idTokenExp = this.getIdTokenExpiration() || Number.MAX_VALUE; var accessTokenExp = this.getAccessTokenExpiration() || Number.MAX_VALUE; var useAccessTokenExp = accessTokenExp <= idTokenExp; if (this.hasValidAccessToken() && useAccessTokenExp) { this.setupAccessTokenTimer(); } if (this.hasValidIdToken() && !useAccessTokenExp) { this.setupIdTokenTimer(); } }; OAuthService.prototype.setupAccessTokenTimer = function () { var _this = this; var expiration = this.getAccessTokenExpiration(); var storedAt = this.getAccessTokenStoredAt(); var timeout = this.calcTimeout(storedAt, expiration); this.ngZone.runOutsideAngular(function () { _this.accessTokenTimeoutSubscription = of(new OAuthInfoEvent('token_expires', 'access_token')) .pipe(delay(timeout)) .subscribe(function (e) { _this.ngZone.run(function () { _this.eventsSubject.next(e); }); }); }); }; OAuthService.prototype.setupIdTokenTimer = function () { var _this = this; var expiration = this.getIdTokenExpiration(); var storedAt = this.getIdTokenStoredAt(); var timeout = this.calcTimeout(storedAt, expiration); this.ngZone.runOutsideAngular(function () { _this.idTokenTimeoutSubscription = of(new OAuthInfoEvent('token_expires', 'id_token')) .pipe(delay(timeout)) .subscribe(function (e) { _this.ngZone.run(function () { _this.eventsSubject.next(e); }); }); }); }; OAuthService.prototype.clearAccessTokenTimer = function () { if (this.accessTokenTimeoutSubscription) { this.accessTokenTimeoutSubscription.unsubscribe(); } }; OAuthService.prototype.clearIdTokenTimer = function () { if (this.idTokenTimeoutSubscription) { this.idTokenTimeoutSubscription.unsubscribe(); } }; OAuthService.prototype.calcTimeout = function (storedAt, expiration) { var delta = (expiration - storedAt) * this.timeoutFactor; return delta; }; OAuthService.prototype.setStorage = function (storage) { this._storage = storage; this.configChanged(); }; OAuthService.prototype.loadDiscoveryDocument = function (fullUrl) { var _this = this; if (fullUrl === void 0) { fullUrl = null; } return new Promise(function (resolve, reject) { if (!fullUrl) { fullUrl = _this.issuer || ''; if (!fullUrl.endsWith('/')) { fullUrl += '/'; } fullUrl += '.well-known/openid-configuration'; } if (!_this.validateUrlForHttps(fullUrl)) { reject('issuer must use Https. Also check property requireHttps.'); return; } _this.http.get(fullUrl).subscribe(function (doc) { if (!_this.validateDiscoveryDocument(doc)) { _this.eventsSubject.next(new OAuthErrorEvent('discovery_document_validation_error', null)); reject('discovery_document_validation_error'); return; } _this.loginUrl = doc.authorization_endpoint; _this.logoutUrl = doc.end_session_endpoint || _this.logoutUrl; _this.grantTypesSupported = doc.grant_types_supported; _this.issuer = doc.issuer; _this.tokenEndpoint = doc.token_endpoint; _this.userinfoEndpoint = doc.userinfo_endpoint; _this.jwksUri = doc.jwks_uri; _this.sessionCheckIFrameUrl = doc.check_session_iframe || _this.sessionCheckIFrameUrl; _this.discoveryDocumentLoaded = true; _this.discoveryDocumentLoadedSubject.next(doc); if (_this.sessionChecksEnabled) { _this.restartSessionChecksIfStillLoggedIn(); } _this.loadJwks() .then(function (jwks) { var result = { discoveryDocument: doc, jwks: jwks }; var event = new OAuthSuccessEvent('discovery_document_loaded', result); _this.eventsSubject.next(event); resolve(event); return; }) .catch(function (err) { _this.eventsSubject.next(new OAuthErrorEvent('discovery_document_load_error', err)); reject(err); return; }); }, function (err) { console.error('error loading discovery document', err); _this.eventsSubject.next(new OAuthErrorEvent('discovery_document_load_error', err)); reject(err); }); }); }; OAuthService.prototype.loadJwks = function () { var _this = this; return new Promise(function (resolve, reject) { if (_this.jwksUri) { _this.http.get(_this.jwksUri).subscribe(function (jwks) { _this.jwks = jwks; _this.eventsSubject.next(new OAuthSuccessEvent('discovery_document_loaded')); resolve(jwks); }, function (err) { console.error('error loading jwks', err); _this.eventsSubject.next(new OAuthErrorEvent('jwks_load_error', err)); reject(err); }); } else { resolve(null); } }); }; OAuthService.prototype.validateDiscoveryDocument = function (doc) { var errors; if (!this.skipIssuerCheck && doc.issuer !== this.issuer) { console.error('invalid issuer in discovery document', 'expected: ' + this.issuer, 'current: ' + doc.issuer); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.authorization_endpoint); if (errors.length > 0) { console.error('error validating authorization_endpoint in discovery document', errors); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.end_session_endpoint); if (errors.length > 0) { console.error('error validating end_session_endpoint in discovery document', errors); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.token_endpoint); if (errors.length > 0) { console.error('error validating token_endpoint in discovery document', errors); } errors = this.validateUrlFromDiscoveryDocument(doc.userinfo_endpoint); if (errors.length > 0) { console.error('error validating userinfo_endpoint in discovery document', errors); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.jwks_uri); if (errors.length > 0) { console.error('error validating jwks_uri in discovery document', errors); return false; } if (this.sessionChecksEnabled && !doc.check_session_iframe) { console.warn('sessionChecksEnabled is activated but discovery document' + ' does not contain a check_session_iframe field'); } return true; }; OAuthService.prototype.fetchTokenUsingPasswordFlowAndLoadUserProfile = function (userName, password, headers) { var _this = this; if (headers === void 0) { headers = new HttpHeaders(); } return this.fetchTokenUsingPasswordFlow(userName, password, headers).then(function () { return _this.loadUserProfile(); }); }; OAuthService.prototype.loadUserProfile = function () { var _this = this; if (!this.hasValidAccessToken()) { throw new Error('Can not load User Profile without access_token'); } var userinfoEndpoint = this.customUserinfoEndpoint ? this.customUserinfoEndpoint : this.userinfoEndpoint; if (!this.validateUrlForHttps(userinfoEndpoint)) { throw new Error('userinfoEndpoint must use Http. Also check property requireHttps.'); } var customBearer = this.customUserinfoEndpoint ? ('Bearer ' + this.getIdToken()) : ('Bearer ' + this.getAccessToken()); return new Promise(function (resolve, reject) { var headers = new HttpHeaders().set('Authorization', customBearer); _this.http.get(userinfoEndpoint, { headers: headers }).subscribe(function (info) { _this.debug('userinfo received', info); var existingClaims = _this.getIdentityClaims() || {}; if (!_this.skipSubjectCheck) { if (_this.oidc && (!existingClaims['sub'] || info.sub !== existingClaims['sub'])) { var err = 'if property oidc is true, the received user-id (sub) has to be the user-id ' + 'of the user that has logged in with oidc.\n' + 'if you are not using oidc but just oauth2 password flow set oidc to false'; reject(err); return; } } info = Object.assign({}, existingClaims, info); _this._storage.setItem('id_token_claims_obj', JSON.stringify(info)); _this.eventsSubject.next(new OAuthSuccessEvent('user_profile_loaded')); resolve(info); }, function (err) { console.error('error loading user info', err); _this.eventsSubject.next(new OAuthErrorEvent('user_profile_load_error', err)); reject(err); }); }); }; OAuthService.prototype.fetchTokenUsingPasswordFlow = function (userName, password, headers) { var _this = this; if (headers === void 0) { headers = new HttpHeaders(); } if (!this.validateUrlForHttps(this.tokenEndpoint)) { throw new Error('tokenEndpoint must use Http. Also check property requireHttps.'); } return new Promise(function (resolve, reject) { var params = new HttpParams({ encoder: new WebHttpUrlEncodingCodec() }) .set('grant_type', 'password') .set('scope', _this.scope) .set('username', userName) .set('password', password); if (_this.useHttpBasicAuthForPasswordFlow) { var header = btoa(_this.clientId + ":" + _this.dummyClientSecret); headers = headers.set('Authentication', 'BASIC ' + header); } if (!_this.useHttpBasicAuthForPasswordFlow) { params = params.set('client_id', _this.clientId); } if (!_this.useHttpBasicAuthForPasswordFlow && _this.dummyClientSecret) { params = params.set('client_secret', _this.dummyClientSecret); } if (_this.customQueryParams) { try { for (var _a = __values(Object.getOwnPropertyNames(_this.customQueryParams)), _b = _a.next(); !_b.done; _b = _a.next()) { var key = _b.value; params = params.set(key, _this.customQueryParams[key]); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_b && !_b.done && (_c = _a.return)) _c.call(_a); } finally { if (e_1) throw e_1.error; } } } headers = headers.set('Content-Type', 'application/x-www-form-urlencoded'); _this.http .post(_this.tokenEndpoint, params, { headers: headers }) .subscribe(function (tokenResponse) { _this.debug('tokenResponse', tokenResponse); _this.storeAccessTokenResponse(tokenResponse.access_token, tokenResponse.refresh_token, tokenResponse.expires_in, tokenResponse.scope); _this.eventsSubject.next(new OAuthSuccessEvent('token_received')); resolve(tokenResponse); }, function (err) { console.error('Error performing password flow', err); _this.eventsSubject.next(new OAuthErrorEvent('token_error', err)); reject(err); }); var e_1, _c; }); }; OAuthService.prototype.refreshToken = function () { var params = new HttpParams() .set('grant_type', 'refresh_token') .set('refresh_token', this._storage.getItem('refresh_token')) .set('scope', this.scope); var savedNonce = this._storage.getItem('nonce'); if (savedNonce && this.oidc) { params = params.set('nonce', savedNonce); } if (this.dummyClientSecret) { params = params.set('client_secret', this.dummyClientSecret); } return this.fetchToken(params); }; OAuthService.prototype.getTokenFromCode = function (code) { var params = new HttpParams() .set('grant_type', 'authorization_code') .set('code', code) .set('redirect_uri', this.redirectUri); return this.fetchToken(params); }; OAuthService.prototype.fetchToken = function (params) { var _this = this; if (!this.validateUrlForHttps(this.tokenEndpoint)) { throw new Error('tokenEndpoint must use Http. Also check property requireHttps.'); } return new Promise(function (resolve, reject) { params = params.set('client_id', _this.clientId); if (_this.customQueryParams) { try { for (var _a = __values(Object.getOwnPropertyNames(_this.customQueryParams)), _b = _a.next(); !_b.done; _b = _a.next()) { var key = _b.value; params = params.set(key, _this.customQueryParams[key]); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_b && !_b.done && (_c = _a.return)) _c.call(_a); } finally { if (e_2) throw e_2.error; } } } var headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); _this.http.post(_this.tokenEndpoint, params, { headers: headers }).subscribe(function (tokenResponse) { _this.debug('refresh tokenResponse', tokenResponse); _this.storeAccessTokenResponse(tokenResponse.access_token, tokenResponse.refresh_token, tokenResponse.expires_in, tokenResponse.scope); if (_this.oidc && tokenResponse.id_token) { _this.processIdToken(tokenResponse.id_token, tokenResponse.access_token).then(function (result) { _this.storeIdToken(result); _this.eventsSubject.next(new OAuthSuccessEvent('token_received')); _this.eventsSubject.next(new OAuthSuccessEvent('token_refreshed')); resolve(tokenResponse); }) .catch(function (reason) { _this.eventsSubject.next(new OAuthErrorEvent('token_validation_error', reason)); console.error('Error validating tokens'); console.error(reason); reject(reason); }); } else { _this.eventsSubject.next(new OAuthSuccessEvent('token_received')); _this.eventsSubject.next(new OAuthSuccessEvent('token_refreshed')); resolve(tokenResponse); } }, function (err) { console.error('Error getting token', err); _this.eventsSubject.next(new OAuthErrorEvent('token_refresh_error', err)); reject(err); }); var e_2, _c; }); }; OAuthService.prototype.removeSilentRefreshEventListener = function () { if (this.silentRefreshPostMessageEventListener) { window.removeEventListener('message', this.silentRefreshPostMessageEventListener); this.silentRefreshPostMessageEventListener = null; } }; OAuthService.prototype.setupSilentRefreshEventListener = function () { var _this = this; this.removeSilentRefreshEventListener(); this.silentRefreshPostMessageEventListener = function (e) { var expectedPrefix = '#'; if (_this.silentRefreshMessagePrefix) { expectedPrefix += _this.silentRefreshMessagePrefix; } if (!e || !e.data || typeof e.data !== 'string') { return; } var prefixedMessage = e.data; if (!prefixedMessage.startsWith(expectedPrefix)) { return; } var message = '#' + prefixedMessage.substr(expectedPrefix.length); _this.tryLogin({ customHashFragment: message, preventClearHashAfterLogin: true, onLoginError: function (err) { _this.eventsSubject.next(new OAuthErrorEvent('silent_refresh_error', err)); }, onTokenReceived: function () { _this.eventsSubject.next(new OAuthSuccessEvent('silently_refreshed')); } }).catch(function (err) { return _this.debug('tryLogin during silent refresh failed', err); }); }; window.addEventListener('message', this.silentRefreshPostMessageEventListener); }; OAuthService.prototype.silentRefresh = function (params, noPrompt) { var _this = this; if (params === void 0) { params = {}; } if (noPrompt === void 0) { noPrompt = true; } var claims = this.getIdentityClaims() || {}; if (this.useIdTokenHintForSilentRefresh && this.hasValidIdToken()) { params['id_token_hint'] = this.getIdToken(); } if (!this.validateUrlForHttps(this.loginUrl)) { throw new Error('tokenEndpoint must use Https. Also check property requireHttps.'); } if (typeof document === 'undefined') { throw new Error('silent refresh is not supported on this platform'); } var existingIframe = document.getElementById(this.silentRefreshIFrameName); if (existingIframe) { document.body.removeChild(existingIframe); } this.silentRefreshSubject = claims['sub']; var iframe = document.createElement('iframe'); iframe.id = this.silentRefreshIFrameName; this.setupSilentRefreshEventListener(); var redirectUri = this.silentRefreshRedirectUri || this.redirectUri; this.createLoginUrl(null, null, redirectUri, noPrompt, params).then(function (url) { iframe.setAttribute('src', url); if (!_this.silentRefreshShowIFrame) { iframe.style['display'] = 'none'; } document.body.appendChild(iframe); }); var errors = this.events.pipe(filter(function (e) { return e instanceof OAuthErrorEvent; }), first()); var success = this.events.pipe(filter(function (e) { return e.type === 'silently_refreshed'; }), first()); var timeout = of(new OAuthErrorEvent('silent_refresh_timeout', null)).pipe(delay(this.silentRefreshTimeout)); return race([errors, success, timeout]) .pipe(tap(function (e) { if (e.type === 'silent_refresh_timeout') { _this.eventsSubject.next(e); } }), map(function (e) { if (e instanceof OAuthErrorEvent) { throw e; } return e; })) .toPromise(); }; OAuthService.prototype.canPerformSessionCheck = function () { if (!this.sessionChecksEnabled) { return false; } if (!this.sessionCheckIFrameUrl) { console.warn('sessionChecksEnabled is activated but there ' + 'is no sessionCheckIFrameUrl'); return false; } var sessionState = this.getSessionState(); if (!sessionState) { console.warn('sessionChecksEnabled is activated but there ' + 'is no session_state'); return false; } if (typeof document === 'undefined') { return false; } return true; }; OAuthService.prototype.setupSessionCheckEventListener = function () { var _this = this; this.removeSessionCheckEventListener(); this.sessionCheckEventListener = function (e) { var origin = e.origin.toLowerCase(); var issuer = _this.issuer.toLowerCase(); _this.debug('sessionCheckEventListener'); if (!issuer.startsWith(origin)) { _this.debug('sessionCheckEventListener', 'wrong origin', origin, 'expected', issuer); } switch (e.data) { case 'unchanged': _this.handleSessionUnchanged(); break; case 'changed': _this.handleSessionChange(); break; case 'error': _this.handleSessionError(); break; } _this.debug('got info from session check inframe', e); }; window.addEventListener('message', this.sessionCheckEventListener); }; OAuthService.prototype.handleSessionUnchanged = function () { this.debug('session check', 'session unchanged'); }; OAuthService.prototype.handleSessionChange = function () { var _this = this; this.eventsSubject.next(new OAuthInfoEvent('session_changed')); this.stopSessionCheckTimer(); if (this.silentRefreshRedirectUri) { this.silentRefresh().catch(function (_) { return _this.debug('silent refresh failed after session changed'); }); this.waitForSilentRefreshAfterSessionChange(); } else { this.eventsSubject.next(new OAuthInfoEvent('session_terminated')); this.logOut(true); } }; OAuthService.prototype.waitForSilentRefreshAfterSessionChange = function () { var _this = this; this.events .pipe(filter(function (e) { return e.type === 'silently_refreshed' || e.type === 'silent_refresh_timeout' || e.type === 'silent_refresh_error'; }), first()) .subscribe(function (e) { if (e.type !== 'silently_refreshed') { _this.debug('silent refresh did not work after session changed'); _this.eventsSubject.next(new OAuthInfoEvent('session_terminated')); _this.logOut(true); } }); }; OAuthService.prototype.handleSessionError = function () { this.stopSessionCheckTimer(); this.eventsSubject.next(new OAuthInfoEvent('session_error')); }; OAuthService.prototype.removeSessionCheckEventListener = function () { if (this.sessionCheckEventListener) { window.removeEventListener('message', this.sessionCheckEventListener); this.sessionCheckEventListener = null; } }; OAuthService.prototype.initSessionCheck = function () { if (!this.canPerformSessionCheck()) { return; } var existingIframe = document.getElementById(this.sessionCheckIFrameName); if (existingIframe) { document.body.removeChild(existingIframe); } var iframe = document.createElement('iframe'); iframe.id = this.sessionCheckIFrameName; this.setupSessionCheckEventListener(); var url = this.sessionCheckIFrameUrl; iframe.setAttribute('src', url); iframe.style.display = 'none'; document.body.appendChild(iframe); this.startSessionCheckTimer(); }; OAuthService.prototype.startSessionCheckTimer = function () { this.stopSessionCheckTimer(); this.sessionCheckTimer = setInterval(this.checkSession.bind(this), this.sessionCheckIntervall); }; OAuthService.prototype.stopSessionCheckTimer = function () { if (this.sessionCheckTimer) { clearInterval(this.sessionCheckTimer); this.sessionCheckTimer = null; } }; OAuthService.prototype.checkSession = function () { var iframe = document.getElementById(this.sessionCheckIFrameName); if (!iframe) { console.warn('checkSession did not find iframe', this.sessionCheckIFrameName); } var sessionState = this.getSessionState(); if (!sessionState) { this.stopSessionCheckTimer(); } var message = this.clientId + ' ' + sessionState; iframe.contentWindow.postMessage(message, this.issuer); }; OAuthService.prototype.createLoginUrl = function (state, loginHint, customRedirectUri, noPrompt, params) { if (state === void 0) { state = ''; } if (loginHint === void 0) { loginHint = ''; } if (customRedirectUri === void 0) { customRedirectUri = ''; } if (noPrompt === void 0) { noPrompt = false; } if (params === void 0) { params = {}; } var that = this; var redirectUri; if (customRedirectUri) { redirectUri = customRedirectUri; } else { redirectUri = this.redirectUri; } var nonce = null; if (!this.disableNonceCheck) { nonce = this.createAndSaveNonce(); if (state) { state = nonce + this.config.nonceStateSeparator + state; } else { state = nonce; } } if (!this.requestAccessToken && !this.oidc) { throw new Error('Either requestAccessToken or oidc or both must be true'); } this.responseType = this.getResponseType(this.inImplicitFlow); var seperationChar = that.loginUrl.indexOf('?') > -1 ? '&' : '?'; var scope = that.scope; if (this.oidc && !scope.match(/(^|\s)openid($|\s)/)) { scope = 'openid ' + scope; } var url = that.loginUrl + seperationChar + 'response_type=' + encodeURIComponent(that.responseType) + '&client_id=' + encodeURIComponent(that.clientId) + '&state=' + encodeURIComponent(state) + '&redirect_uri=' + encodeURIComponent(redirectUri) + '&scope=' + encodeURIComponent(scope); if (loginHint) { url += '&login_hint=' + encodeURIComponent(loginHint); } if (that.resource) { url += '&resource=' + encodeURIComponent(that.resource); } if (nonce && this.oidc) { url += '&nonce=' + encodeURIComponent(nonce); } if (noPrompt) { url += '&prompt=none'; } try { for (var _a = __values(Object.keys(params)), _b = _a.next(); !_b.done; _b = _a.next()) { var key = _b.value; url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_b && !_b.done && (_c = _a.return)) _c.call(_a); } finally { if (e_3) throw e_3.error; } } if (this.customQueryParams) { try { for (var _d = __values(Object.getOwnPropertyNames(this.customQueryParams)), _e = _d.next(); !_e.done; _e = _d.next()) { var key = _e.value; url += '&' + key + '=' + encodeURIComponent(this.customQueryParams[key]); } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_e && !_e.done && (_f = _d.return)) _f.call(_d); } finally { if (e_4) throw e_4.error; } } } return Promise.resolve(url); var e_3, _c, e_4, _f; }; OAuthService.prototype.initImplicitFlowInternal = function (additionalState, params) { var _this = this; if (additionalState === void 0) { additionalState = ''; } if (params === void 0) { params = ''; } if (this.inImplicitFlow) { return; } this.inImplicitFlow = true; if (!this.validateUrlForHttps(this.loginUrl)) { throw new Error('loginUrl must use Http. Also check property requireHttps.'); } var addParams = {}; var loginHint = null; if (typeof params === 'string') { loginHint = params; } else if (typeof params === 'object') { addParams = params; } this.createLoginUrl(additionalState, loginHint, null, false, addParams) .then(function (url) { location.href = url; }) .catch(function (error) { console.error('Error in initImplicitFlow'); console.error(error); _this.inImplicitFlow = false; }); }; OAuthService.prototype.initImplicitFlow = function (additionalState, params) { var _this = this; if (additionalState === void 0) { additionalState = ''; } if (params === void 0) { params = ''; } if (this.loginUrl !== '') { this.initImplicitFlowInternal(additionalState, params); } else { this.events .pipe(filter(function (e) { return e.type === 'discovery_document_loaded'; })) .subscribe(function (_) { return _this.initImplicitFlowInternal(additionalState, params); }); } }; OAuthService.prototype.initAuthorizationCodeFlow = function () { var _this = this; if (this.loginUrl !== '') { this.initAuthorizationCodeFlowInternal(); } else { this.events.pipe(filter(function (e) { return e.type === 'discovery_document_loaded'; })) .subscribe(function (_) { return _this.initAuthorizationCodeFlowInternal(); }); } }; OAuthService.prototype.initAuthorizationCodeFlowInternal = function () { if (!this.validateUrlForHttps(this.loginUrl)) { throw new Error('loginUrl must use Http. Also check property requireHttps.'); } this.createLoginUrl('', '', null, false, {}).then(function (url) { location.href = url; }) .catch(function (error) { console.error('Error in initAuthorizationCodeFlow'); console.error(error); }); }; OAuthService.prototype.getResponseType = function (implicit) { if (implicit) { if (this.oidc && this.requestAccessToken) { return 'id_token token'; } else if (this.oidc && !this.requestAccessToken) { return 'id_token'; } else { return 'token'; } } else { return 'code'; } }; OAuthService.prototype.callOnTokenReceivedIfExists = function (options) { var that = this; if (options.onTokenReceived) { var tokenParams = { idClaims: that.getIdentityClaims(), idToken: that.getIdToken(), accessToken: that.getAccessToken(), state: that.state }; options.onTokenReceived(tokenParams); } }; OAuthService.prototype.storeAccessTokenResponse = function (accessToken, refreshToken, expiresIn, grantedScopes) { this._storage.setItem('access_token', accessToken); if (grantedScopes) { this._storage.setItem('granted_scopes', JSON.stringify(grantedScopes.split('+'))); } this._storage.setItem('access_token_stored_at', '' + Date.now()); if (expiresIn) { var expiresInMilliSeconds = expiresIn * 1000; var now = new Date(); var expiresAt = now.getTime() + expiresInMilliSeconds; this._storage.setItem('expires_at', '' + expiresAt); } if (refreshToken) { this._storage.setItem('refresh_token', refreshToken); } }; OAuthService.prototype.tryLogin = function (options) { if (options === void 0) { options = null; } options = options || {}; if (!this.requestAccessToken && !this.oidc) { return Promise.reject('Either requestAccessToken or oidc or both must be true.'); } if (window.location.search && (window.location.search.startsWith('?code=') || window.location.search.includes('&code='))) { return this.tryLoginAuthorizationCode(); } else { return this.tryLoginImplicit(options); } }; OAuthService.prototype.tryLoginAuthorizationCode = function () { var _this = this; var parameter = window.location.search.split('?')[1].split('&'); var codeParam = parameter.filter(function (param) { return param.includes('code='); }); var code = codeParam.length ? codeParam[0].split('code=')[1] : undefined; if (code) { return new Promise(function (resolve, reject) { _this.getTokenFromCode(code).then(function (result) { resolve(); }).catch(function (err) { reject(err); }); }); } else { return Promise.resolve(); } }; OAuthService.prototype.tryLoginImplicit = function (options) { var _this = this; if (options === void 0) { options = null; } options = options || {}; var parts; if (options.customHashFragment) { parts = this.urlHelper.getHashFragmentParams(options.customHashFragment); } else { parts = this.urlHelper.getHashFragmentParams(); } this.debug('parsed url', parts); var state = parts['state']; var nonceInState = state; if (state) { var idx = state.indexOf(this.config.nonceStateSeparator); if (idx > -1) { nonceInState = state.substr(0, idx); this.state = state.substr(idx + this.config.nonceStateSeparator.length); } } if (parts['error']) { this.debug('error trying to login'); this.handleLoginError(options, parts); var err = new OAuthErrorEvent('token_error', {}, parts); this.eventsSubject.next(err); return Promise.reject(err); } var accessToken = parts['access_token']; var idToken = parts['id_token']; var sessionState = parts['session_state']; var grantedScopes = parts['scope']; if (!this.requestAccessToken && !this.oidc) { return Promise.reject('Either requestAccessToken or oidc or both must be true.'); } if (this.requestAccessToken && !accessToken) { return Promise.resolve(); } if (this.requestAccessToken && !options.disableOAuth2StateCheck && !state) { return Promise.res