p))return!1;var v=f.get(e);if(v&&f.get(t))return v==t;var d=-1,g=!0,y=r&s?new n:void 0;for(f.set(e,t),f.set(t,e);++d1&&void 0!==arguments[1]?arguments[1]:{},r=e;return Object.keys(t).forEach(function(e){var n=t[e];-1===[void 0,null,""].indexOf(n)&&(r+="".concat(-1===r.indexOf("?")?"?":"&").concat(e,"=").concat(encodeURIComponent(n)))}),r}},{key:"resolveUrl",value:function(e){return this.$$urlDocument||(this.$$urlDocument=document.implementation.createHTMLDocument("url"),this.$$urlBase=this.$$urlDocument.createElement("base"),this.$$urlAnchor=this.$$urlDocument.createElement("a"),this.$$urlDocument.head.appendChild(this.$$urlBase)),this.$$urlBase.href=window.location.protocol+"//"+window.location.host,this.$$urlAnchor.href=e.replace(/ /g,"%20"),this.$$urlAnchor.href.replace(/\/$/,"")}},{key:"checkForMatchingUrl",value:function(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],r=this.resolveUrl(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:"salte-auth",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:600,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:400,i=window.innerHeight/2-n/2+window.screenTop,u=window.innerWidth/2-o/2+window.screenLeft,s=window.open(e,r,"height=".concat(n,", width=").concat(o,", status=yes, toolbar=no, menubar=no, location=no, top=").concat(i,", left=").concat(u));return s?(s.focus(),new Promise(function(e){var r=setInterval(function(){try{if(!s.closed){var n=t.$$config.redirectUrl&&t.$$config.redirectUrl.loginUrl||t.$$config.redirectUrl,o=t.$$config.redirectUrl&&t.$$config.redirectUrl.logoutUrl||t.$$config.redirectUrl;if(0!==s.location.href.indexOf(n)||0!==s.location.href.indexOf(o))return;location.hash=s.location.hash,s.close()}clearInterval(r),setTimeout(e)}catch(e){}},100)})):Promise.reject(new ReferenceError("We were unable to open the popup window, its likely that the request was blocked."))}},{key:"openNewTab",value:function(e){var t=this,r=window.open(e,"_blank");return r?(r.name="salte-auth",r.focus(),new Promise(function(e){var n=setInterval(function(){try{if(!r.closed){var o=t.$$config.redirectUrl&&t.$$config.redirectUrl.loginUrl||t.$$config.redirectUrl,i=t.$$config.redirectUrl&&t.$$config.redirectUrl.logoutUrl||t.$$config.redirectUrl;if(0!==r.location.href.indexOf(o)||0!==r.location.href.indexOf(i))return;location.hash=r.location.hash,r.close()}clearInterval(n),setTimeout(e)}catch(e){}},100)})):Promise.reject(new ReferenceError("We were unable to open the new tab, its likely that the request was blocked."))}},{key:"createIframe",value:function(e,t){var r=document.createElement("iframe");return r.setAttribute("owner","salte-auth"),t?(o()(r.style,{position:"fixed",top:0,bottom:0,left:0,right:0,height:"100%",width:"100%",zIndex:9999,border:"none",opacity:0,transition:"0.5s opacity"}),setTimeout(function(){r.style.opacity=1})):r.style.display="none",r.src=e,document.body.appendChild(r),new Promise(function(e){r.addEventListener("DOMNodeRemoved",function(){setTimeout(e)},{passive:!0})})}},{key:"addXHRInterceptor",value:function(e){this.$interceptors.xhr.push(e)}},{key:"addFetchInterceptor",value:function(e){this.$interceptors.fetch.push(e)}},{key:"$navigate",value:function(e){location.href=e}},{key:"$iframe",get:function(){return window.self===window.top?null:parent.document.querySelector('body > iframe[owner="salte-auth"]')}},{key:"$popup",get:function(){return window.opener&&"salte-auth"===window.name?window:null}},{key:"$hidden",get:function(){return document.hidden}}])&&u(t.prototype,r),n&&u(t,n),e}();t.default=a},function(e,t,r){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){for(var r=0;r} routes A list of secured routes. If true is provided then all routes are secured.\n * @property {Array} endpoints A list of secured endpoints.\n * @property {('auth0'|'azure'|'cognito'|'wso2'|'okta')} provider The identity provider you're using.\n * @property {('iframe'|'redirect'|false)} [loginType='iframe'] The automated login type to use.\n * @property {Function} [redirectLoginCallback] A callback that is invoked when a redirect login fails or succeeds.\n * @property {('session'|'local')} [storageType='session'] The Storage api to keep authenticate information stored in.\n * @property {Boolean|Validation} [validation] Used to disable certain security validations if your provider doesn't support them.\n * @property {Boolean} [autoRefresh=true] Automatically refreshes the users token upon switching tabs or one minute prior to expiration.\n * @property {Number} [autoRefreshBuffer=60000] A number of miliseconds before token expiration to refresh.\n */\n\n/**\n * The configuration for salte auth\n * @typedef {Object} LoginConfig\n * @property {Boolean} [noPrompt=false] Disables login prompts, this should only be used for token renewal!\n * @property {(false|'errors'|'all')} [clear='all'] Whether to clear \"all\" profile information, only \"errors\", or nothing.\n * @property {Boolean} [events=true] Whether events should be fired off if the login is successful or not.\n */\n\n/**\n * Authentication Controller\n */\nclass SalteAuth {\n /**\n * Sets up Salte Auth\n * @param {Config} config configuration for salte auth\n */\n constructor(config) {\n if (window.salte.auth) {\n return window.salte.auth;\n }\n\n if (!config) {\n throw new ReferenceError('A config must be provided.');\n }\n\n /**\n * The supported identity providers\n * @type {Providers}\n * @private\n */\n this.$providers = Providers;\n /**\n * The active authentication promises\n * @private\n */\n this.$promises = {};\n /**\n * The active authentication timeouts\n * @private\n */\n this.$timeouts = {};\n /**\n * The registered listeners\n * @private\n */\n this.$listeners = {};\n /**\n * The configuration for salte auth\n * @type {Config}\n * @private\n */\n this.$config = config;\n this.$config = defaultsDeep(config, this.$provider.defaultConfig, {\n loginType: 'iframe',\n autoRefresh: true,\n autoRefreshBuffer: 60000\n });\n /**\n * Various utility functions for salte auth\n * @type {SalteAuthUtilities}\n * @private\n */\n this.$utilities = new SalteAuthUtilities(this.$config);\n\n /**\n * The user profile for salte auth\n * @type {SalteAuthProfile}\n */\n this.profile = new SalteAuthProfile(this.$config);\n\n /**\n * A mixin built for Web Components\n *\n * @example\n * class MyElement extends auth.mixin(HTMLElement) {\n * constructor() {\n * super();\n *\n * console.log(this.auth); // This is the same as auth\n * console.log(this.user); // This is the same as auth.profile.userInfo.\n * console.log(this.authenticated); // This is the same as auth.profile.idTokenExpired.\n * }\n * }\n */\n this.mixin = SalteAuthMixinGenerator(this);\n\n if (this.$utilities.$iframe) {\n logger('Detected iframe, removing...');\n this.profile.$parseParams();\n parent.document.body.removeChild(this.$utilities.$iframe);\n } else if (this.$utilities.$popup) {\n logger('Popup detected!');\n } else if (this.profile.$redirectUrl && location.href !== this.profile.$redirectUrl) {\n logger('Redirect detected!');\n this.profile.$parseParams();\n const error = this.profile.$validate();\n\n // Delay for an event loop to give users time to register a listener.\n setTimeout(() => {\n const action = this.profile.$actions(this.profile.$state);\n\n if (error) {\n this.profile.$clear();\n } else {\n logger(`Navigating to Redirect URL... (${this.profile.$redirectUrl})`);\n this.$utilities.$navigate(this.profile.$redirectUrl);\n this.profile.$redirectUrl = undefined;\n }\n\n if (action === 'login') {\n this.$fire('login', error || null, this.profile.userInfo);\n } else if (action === 'logout') {\n this.$fire('logout', error);\n }\n\n // TODO(v3.0.0): Remove the `redirectLoginCallback` api from `salte-auth`.\n this.$config.redirectLoginCallback && this.$config.redirectLoginCallback(error);\n });\n } else {\n logger('Setting up interceptors...');\n this.$utilities.addXHRInterceptor((request, data) => {\n if (this.$utilities.checkForMatchingUrl(request.$url, this.$config.endpoints)) {\n return this.retrieveAccessToken().then((accessToken) => {\n request.setRequestHeader('Authorization', `Bearer ${accessToken}`);\n });\n }\n });\n\n this.$utilities.addFetchInterceptor((request) => {\n if (this.$utilities.checkForMatchingUrl(request.url, this.$config.endpoints)) {\n return this.retrieveAccessToken().then((accessToken) => {\n request.headers.set('Authorization', `Bearer ${accessToken}`);\n });\n }\n });\n\n logger('Setting up route change detectors...');\n window.addEventListener('popstate', this.$$onRouteChanged.bind(this), { passive: true });\n document.addEventListener('click', this.$$onRouteChanged.bind(this), { passive: true });\n setTimeout(this.$$onRouteChanged.bind(this));\n\n logger('Setting up automatic renewal of token...');\n this.on('login', (error) => {\n if (error) return;\n\n this.$$refreshToken();\n });\n\n this.on('refresh', (error) => {\n if (error) return;\n\n this.$$refreshToken();\n });\n\n this.on('logout', () => {\n clearTimeout(this.$timeouts.refresh);\n });\n\n if (!this.profile.idTokenExpired) {\n this.$$refreshToken();\n }\n\n document.addEventListener('visibilitychange', this.$$onVisibilityChanged.bind(this), {\n passive: true\n });\n\n this.$fire('create', null, this);\n }\n\n // TODO(v3.0.0): Revoke singleton status from `salte-auth`.\n window.salte.auth = this;\n\n if (this.$config.redirectLoginCallback) {\n console.warn(`The \"redirectLoginCallback\" api has been deprecated in favor of the \"on\" api, see http://bit.ly/salte-auth-on for more info.`);\n }\n }\n\n /**\n * Returns the configured provider\n * @type {Class|Object}\n * @private\n */\n get $provider() {\n if (!this.$config.provider) {\n throw new ReferenceError('A provider must be specified');\n }\n\n if (typeof this.$config.provider === 'string') {\n const provider = this.$providers[this.$config.provider];\n if (!provider) {\n throw new ReferenceError(`Unknown Provider (${this.$config.provider})`);\n }\n return provider;\n }\n\n return this.$config.provider;\n }\n\n /**\n * The authentication url to retrieve the access token\n * @type {String}\n * @private\n */\n get $accessTokenUrl() {\n this.profile.$localState = uuid.v4();\n this.profile.$nonce = uuid.v4();\n\n let authorizeEndpoint = `${this.$config.providerUrl}/authorize`;\n if (this.$provider.authorizeEndpoint) {\n authorizeEndpoint = this.$provider.authorizeEndpoint.call(this, this.$config);\n }\n\n return this.$utilities.createUrl(authorizeEndpoint, assign({\n 'state': this.profile.$localState,\n 'nonce': this.profile.$nonce,\n 'response_type': 'token',\n 'redirect_uri': this.$config.redirectUrl && this.$config.redirectUrl.loginUrl || this.$config.redirectUrl,\n 'client_id': this.$config.clientId,\n 'scope': this.$config.scope,\n 'prompt': 'none'\n }, this.$config.queryParams));\n }\n\n /**\n * The authentication url to retrieve the id token\n * @param {Boolean} refresh Whether this request is intended to refresh the token.\n * @return {String} the computed login url\n * @private\n */\n $loginUrl(refresh) {\n this.profile.$localState = uuid.v4();\n this.profile.$nonce = uuid.v4();\n\n let authorizeEndpoint = `${this.$config.providerUrl}/authorize`;\n if (this.$provider.authorizeEndpoint) {\n authorizeEndpoint = this.$provider.authorizeEndpoint.call(this, this.$config);\n }\n\n return this.$utilities.createUrl(authorizeEndpoint, assign({\n 'state': this.profile.$localState,\n 'nonce': this.profile.$nonce,\n 'response_type': this.$config.responseType,\n 'redirect_uri': this.$config.redirectUrl && this.$config.redirectUrl.loginUrl || this.$config.redirectUrl,\n 'client_id': this.$config.clientId,\n 'scope': this.$config.scope,\n 'prompt': refresh ? 'none' : undefined\n }, this.$config.queryParams));\n }\n\n /**\n * The url to logout of the configured provider\n * @type {String}\n * @private\n */\n get $deauthorizeUrl() {\n return this.$provider.deauthorizeUrl.call(this, defaultsDeep(this.$config, {\n idToken: this.profile.$idToken\n }));\n }\n\n /**\n * Listens for an event to be invoked.\n * @param {('login'|'logout'|'refresh'|'expired')} eventType the event to listen for.\n * @param {Function} callback A callback that fires when the specified event occurs.\n *\n * @example\n * auth.on('login', (error, user) => {\n * if (error) {\n * console.log('something bad happened!');\n * }\n *\n * console.log(user); // This is the same as auth.profile.userInfo.\n * });\n *\n * @example\n * window.addEventListener('salte-auth-login', (event) => {\n * if (event.detail.error) {\n * console.log('something bad happened!');\n * }\n *\n * console.log(event.detail.data); // This is the same as auth.profile.userInfo.\n * });\n */\n on(eventType, callback) {\n if (['login', 'logout', 'refresh', 'expired'].indexOf(eventType) === -1) {\n throw new ReferenceError(`Unknown Event Type (${eventType})`);\n } else if (typeof callback !== 'function') {\n throw new ReferenceError('Invalid callback provided!');\n }\n\n this.$listeners[eventType] = this.$listeners[eventType] || [];\n this.$listeners[eventType].push(callback);\n }\n\n /**\n * Deregister a callback previously registered.\n * @param {('login'|'logout'|'refresh'|'expired')} eventType the event to deregister.\n * @param {Function} callback A callback that fires when the specified event occurs.\n *\n * @example\n * const someFunction = function() {};\n *\n * auth.on('login', someFunction);\n *\n * auth.off('login', someFunction);\n */\n off(eventType, callback) {\n if (['login', 'logout', 'refresh', 'expired'].indexOf(eventType) === -1) {\n throw new ReferenceError(`Unknown Event Type (${eventType})`);\n } else if (typeof callback !== 'function') {\n throw new ReferenceError('Invalid callback provided!');\n }\n\n const eventListeners = this.$listeners[eventType];\n if (!eventListeners || !eventListeners.length) return;\n\n const index = eventListeners.indexOf(callback);\n eventListeners.splice(index, 1);\n }\n\n /**\n * Fires off an event to a given set of listeners\n * @param {String} eventType The event that occurred.\n * @param {Error} error The error tied to this event.\n * @param {*} data The data tied to this event.\n * @private\n */\n $fire(eventType, error, data) {\n const event = document.createEvent('Event');\n event.initEvent(`salte-auth-${eventType}`, false, true);\n event.detail = { error, data };\n window.dispatchEvent(event);\n\n const eventListeners = this.$listeners[eventType];\n\n if (!eventListeners || !eventListeners.length) return;\n\n eventListeners.forEach((listener) => listener(error, data));\n }\n\n /**\n * Authenticates using the iframe-based OAuth flow.\n * @param {Boolean|LoginConfig} config Whether this request is intended to refresh the token.\n * @return {Promise