UNPKG

angular-oauth2-oidc

Version:

Support for OAuth 2 and OpenId Connect (OIDC) in Angular.

1,159 lines (1,150 loc) 77.7 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common/http'), require('rxjs'), require('rxjs/operators'), require('@angular/common'), require('jsrsasign')) : typeof define === 'function' && define.amd ? define('angular-oauth2-oidc', ['exports', '@angular/core', '@angular/common/http', 'rxjs', 'rxjs/operators', '@angular/common', 'jsrsasign'], factory) : (factory((global['angular-oauth2-oidc'] = {}),global.ng.core,global.ng.common.http,global.rxjs,global.Rx.Observable.prototype,global.ng.common,global.jsrsasign)); }(this, (function (exports,core,http,rxjs,operators,common,jsrsasign) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __awaiter(thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } function __values(o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } var LoginOptions = /** @class */ (function () { function LoginOptions() { this.preventClearHashAfterLogin = false; } return LoginOptions; }()); var OAuthLogger = /** @class */ (function () { function OAuthLogger() { } return OAuthLogger; }()); 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) { return __awaiter(this, void 0, void 0, function () { var hashAlg, tokenHash, leftMostHalf, tokenHashBase64, atHash, claimsAtHash; return __generator(this, function (_a) { switch (_a.label) { case 0: hashAlg = this.inferHashAlgorithm(params.idTokenHeader); return [4 /*yield*/, this.calcHash(params.accessToken, hashAlg)]; case 1: tokenHash = _a.sent(); leftMostHalf = tokenHash.substr(0, tokenHash.length / 2); tokenHashBase64 = btoa(leftMostHalf); atHash = tokenHashBase64 .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); claimsAtHash = params.idTokenClaims['at_hash'].replace(/=/g, ''); if (atHash !== claimsAtHash) { console.error('exptected at_hash: ' + atHash); console.error('actual at_hash: ' + claimsAtHash); } return [2 /*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; var pair; var separatorIndex; var escapedKey; var escapedValue; var key; var 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: core.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.userinfoEndpoint = null; this.responseType = ''; 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.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$$1, storage, tokenValidationHandler, config, urlHelper, logger) { var _this = _super.call(this) || this; _this.ngZone = ngZone; _this.http = http$$1; _this.config = config; _this.urlHelper = urlHelper; _this.logger = logger; _this.discoveryDocumentLoaded = false; _this.state = ''; _this.eventsSubject = new rxjs.Subject(); _this.discoveryDocumentLoadedSubject = new rxjs.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('No OAuthStorage provided and cannot access default (sessionStorage).' + 'Consider providing a custom OAuthStorage implementation in your module.', 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(operators.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(operators.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) { this.logger.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(operators.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 = rxjs.of(new OAuthInfoEvent('token_expires', 'access_token')) .pipe(operators.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 = rxjs.of(new OAuthInfoEvent('token_expires', 'id_token')) .pipe(operators.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, or config value for property requireHttps must allow http'); 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) { _this.logger.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) { _this.logger.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) { this.logger.error('invalid issuer in discovery document', 'expected: ' + this.issuer, 'current: ' + doc.issuer); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.authorization_endpoint); if (errors.length > 0) { this.logger.error('error validating authorization_endpoint in discovery document', errors); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.end_session_endpoint); if (errors.length > 0) { this.logger.error('error validating end_session_endpoint in discovery document', errors); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.token_endpoint); if (errors.length > 0) { this.logger.error('error validating token_endpoint in discovery document', errors); } errors = this.validateUrlFromDiscoveryDocument(doc.userinfo_endpoint); if (errors.length > 0) { this.logger.error('error validating userinfo_endpoint in discovery document', errors); return false; } errors = this.validateUrlFromDiscoveryDocument(doc.jwks_uri); if (errors.length > 0) { this.logger.error('error validating jwks_uri in discovery document', errors); return false; } if (this.sessionChecksEnabled && !doc.check_session_iframe) { this.logger.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 http.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'); } if (!this.validateUrlForHttps(this.userinfoEndpoint)) { throw new Error('userinfoEndpoint must use http, or config value for property requireHttps must allow http'); } return new Promise(function (resolve, reject) { var headers = new http.HttpHeaders().set('Authorization', 'Bearer ' + _this.getAccessToken()); _this.http.get(_this.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) { _this.logger.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 http.HttpHeaders(); } if (!this.validateUrlForHttps(this.tokenEndpoint)) { throw new Error('tokenEndpoint must use http, or config value for property requireHttps must allow http'); } return new Promise(function (resolve, reject) { var e_1, _a; var params = new http.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('Authorization', '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 _b = __values(Object.getOwnPropertyNames(_this.customQueryParams)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; params = params.set(key, _this.customQueryParams[key]); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } 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) { _this.logger.error('Error performing password flow', err); _this.eventsSubject.next(new OAuthErrorEvent('token_error', err)); reject(err); }); }); }; OAuthService.prototype.refreshToken = function () { var _this = this; if (!this.validateUrlForHttps(this.tokenEndpoint)) { throw new Error('tokenEndpoint must use http, or config value for property requireHttps must allow http'); } return new Promise(function (resolve, reject) { var e_2, _a; var params = new http.HttpParams() .set('grant_type', 'refresh_token') .set('client_id', _this.clientId) .set('scope', _this.scope) .set('refresh_token', _this._storage.getItem('refresh_token')); if (_this.dummyClientSecret) { params = params.set('client_secret', _this.dummyClientSecret); } if (_this.customQueryParams) { try { for (var _b = __values(Object.getOwnPropertyNames(_this.customQueryParams)), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; params = params.set(key, _this.customQueryParams[key]); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } } var headers = new http.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); _this.eventsSubject.next(new OAuthSuccessEvent('token_received')); _this.eventsSubject.next(new OAuthSuccessEvent('token_refreshed')); resolve(tokenResponse); }, function (err) { _this.logger.error('Error performing password flow', err); _this.eventsSubject.next(new OAuthErrorEvent('token_refresh_error', err)); reject(err); }); }); }; 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, or config value for property requireHttps must allow http'); } 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(operators.filter(function (e) { return e instanceof OAuthErrorEvent; }), operators.first()); var success = this.events.pipe(operators.filter(function (e) { return e.type === 'silently_refreshed'; }), operators.first()); var timeout = rxjs.of(new OAuthErrorEvent('silent_refresh_timeout', null)).pipe(operators.delay(this.silentRefreshTimeout)); return rxjs.race([errors, success, timeout]) .pipe(operators.tap(function (e) { if (e.type === 'silent_refresh_timeout') { _this.eventsSubject.next(e); } }), operators.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.ngZone.run(function () { _this.handleSessionChange(); }); break; case 'error': _this.ngZone.run(function () { _this.handleSessionError(); }); break; } _this.debug('got info from session check inframe', e); }; this.ngZone.runOutsideAngular(function () { 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(operators.filter(function (e) { return e.type === 'silently_refreshed' || e.type === 'silent_refresh_timeout' || e.type === 'silent_refresh_error'; }), operators.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 () { var _this = this; this.stopSessionCheckTimer(); this.ngZone.runOutsideAngular(function () { _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) { this.logger.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) { var _this = this; 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; } return this.createAndSaveNonce().then(function (nonce) { var e_3, _a, e_4, _b; 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'); } if (_this.config.responseType) { _this.responseType = _this.config.responseType; } else { if (_this.oidc && _this.requestAccessToken) { _this.responseType = 'id_token token'; } else if (_this.oidc && !_this.requestAccessToken) { _this.responseType = 'id_token'; } else { _this.responseType = 'token'; } } 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 (that.oidc) { url += '&nonce=' + encodeURIComponent(nonce); } if (noPrompt) { url += '&prompt=none'; } try { for (var _c = __values(Object.keys(params)), _d = _c.next(); !_d.done; _d = _c.next()) { var key = _d.value; url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_3) throw e_3.error; } } if (_this.customQueryParams) { try { for (var _e = __values(Object.getOwnPropertyNames(_this.customQueryParams)), _f = _e.next(); !_f.done; _f = _e.next()) { var key = _f.value; url += '&' + key + '=' + encodeURIComponent(_this.customQueryParams[key]); } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_f && !_f.done && (_b = _e.return)) _b.call(_e); } finally { if (e_4) throw e_4.error; } } } return url; }); }; 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, or config value for property requireHttps must allow http'); } 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', 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(operators.filter(function (e) { return e.type === 'discovery_document_loaded'; })) .subscribe(function (_) { return _this.initImplicitFlowInternal(additionalState, params); }); } }; OAuthService.prototype.callOnTokenReceivedIfExists = function (options) { var that = this; if (options.onTokenReceived) { var tokenParams = {