From 50481ede262aaa2ac2135cc6e24d21e507ecc872 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 11:34:40 +0200 Subject: [PATCH 01/16] Make library work in iframes, not just in popup windows --- src/oauth/oauth-context.js | 115 +++++++++++++++++++++++++++++++++++++ src/oauth/oauth1.js | 18 +++--- src/oauth/oauth2.js | 14 ++--- src/oauth/popup.js | 88 ---------------------------- src/options.js | 20 +++---- 5 files changed, 141 insertions(+), 114 deletions(-) create mode 100644 src/oauth/oauth-context.js delete mode 100644 src/oauth/popup.js diff --git a/src/oauth/oauth-context.js b/src/oauth/oauth-context.js new file mode 100644 index 0000000..5a75c9c --- /dev/null +++ b/src/oauth/oauth-context.js @@ -0,0 +1,115 @@ +import Promise from '../promise.js' +import { objectExtend, parseQueryString, getFullUrlPath, isUndefined } from '../utils.js' + +/** + * OAuth2 popup/iframe management class + * + * @author Sahat Yalkabov + * @copyright Class mostly taken from https://github.com/sahat/satellizer/blob/master/src/popup.ts + * and adjusted to fit vue-authenticate library + */ +export default class OAuthContext { + constructor (url, name, authContextOptions = {}) { + this.authWindow = null + this.url = url + this.name = name + this.authContextOptions = authContextOptions + } + + open (redirectUri, skipPooling) { + try { + if (this.authContextOptions.iframe) { + this.iframe = document.createElement('iframe') + this.iframe.src = this.url + this.iframeTarget.appendChild(this.iframe) + this.authWindow = this.iframe.contentWindow + } else { + this.authWindow = window.open(this.url, this.name, this._stringifyOptions()) + if (this.authWindow && this.authWindow.focus) { + this.authWindow.focus() + } + } + + if (skipPooling) { + return Promise.resolve() + } else { + return this.pooling(redirectUri) + } + } catch (e) { + return Promise.reject(new Error('Error occurred while opening authentication context')) + } + } + + get iframeTarget () { + if (!this._iframeTarget) { + if (this.authContextOptions.iframeTarget) { + this._iframeTarget = this.authContextOptions.iframeTarget + } else { + this._iframeTarget = document.createElement('div') + this._iframeTarget.style.display = 'none' + document.body.appendChild(this._iframeTarget) + } + } + + return this._iframeTarget + } + + pooling (redirectUri) { + return new Promise((resolve, reject) => { + const redirectUriParser = document.createElement('a') + redirectUriParser.href = redirectUri + const redirectUriPath = getFullUrlPath(redirectUriParser) + + let poolingInterval = setInterval(() => { + if (!this.authContextOptions.iframe) { + if (!this.authWindow || this.authWindow.closed || this.authWindow.closed === undefined) { + clearInterval(poolingInterval) + poolingInterval = null + reject(new Error('Auth popup window closed')) + } + } + + try { + const authWindowPath = getFullUrlPath(this.authWindow.location) + + if (authWindowPath === redirectUriPath) { + if (this.authWindow.location.search || this.authWindow.location.hash) { + const query = parseQueryString(this.authWindow.location.search.substring(1).replace(/\/$/, '')) + const hash = parseQueryString(this.authWindow.location.hash.substring(1).replace(/[\/$]/, '')) + let params = objectExtend({}, query) + params = objectExtend(params, hash) + + if (params.error) { + reject(new Error(params.error)) + } else { + resolve(params) + } + } else { + reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')) + } + + clearInterval(poolingInterval) + poolingInterval = null + if (this.authContextOptions.iframe) { + this.iframeTarget.removeChild(this.iframe) + } else { + this.authWindow.close() + } + } + } catch (e) { + // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. + } + }, 250) + }) + } + + _stringifyOptions () { + let options = [] + for (var optionKey in this.authContextOptions) { + if (!isUndefined(this.authContextOptions[optionKey])) { + options.push(`${optionKey}=${this.authContextOptions[optionKey]}`) + } + } + return options.join(',') + } +} diff --git a/src/oauth/oauth1.js b/src/oauth/oauth1.js index 3014b86..05448bc 100644 --- a/src/oauth/oauth1.js +++ b/src/oauth/oauth1.js @@ -1,4 +1,4 @@ -import OAuthPopup from './popup.js' +import OAuthContext from './oauth-context.js' import { objectExtend, isString, isObject, isFunction, joinUrl } from '../utils.js' const defaultProviderConfig = { @@ -12,7 +12,7 @@ const defaultProviderConfig = { requiredUrlParams: null, defaultUrlParams: null, oauthType: '1.0', - popupOptions: {} + authContextOptions: {} } export default class OAuth { @@ -25,15 +25,15 @@ export default class OAuth { } /** - * Initialize OAuth1 process + * Initialize OAuth1 process * @param {Object} userData User data * @return {Promise} */ init(userData) { - this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions) + this.oauthContext = new OAuthContext('about:blank', this.providerConfig.name, this.providerConfig.authContextOptions) if (window && !window['cordova']) { - this.oauthPopup.open(this.providerConfig.redirectUri, true) + this.oauthContext.open(this.providerConfig.redirectUri, true) } return this.getRequestToken().then((response) => { @@ -69,11 +69,11 @@ export default class OAuth { openPopup(response) { const url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); - this.oauthPopup.popup.location = url + this.oauthContext.popup.location = url if (window && window['cordova']) { - return this.oauthPopup.open(this.providerConfig.redirectUri) + return this.oauthContext.open(this.providerConfig.redirectUri) } else { - return this.oauthPopup.pooling(this.providerConfig.redirectUri) + return this.oauthContext.pooling(this.providerConfig.redirectUri) } } @@ -106,4 +106,4 @@ export default class OAuth { } return parsedParams.join('&'); } -} \ No newline at end of file +} diff --git a/src/oauth/oauth2.js b/src/oauth/oauth2.js index f527ad1..3b6c300 100644 --- a/src/oauth/oauth2.js +++ b/src/oauth/oauth2.js @@ -1,4 +1,4 @@ -import OAuthPopup from './popup.js' +import OAuthContext from './oauth-context.js' import { camelCase, isFunction, isString, objectExtend, joinUrl } from '../utils.js' /** @@ -24,7 +24,7 @@ const defaultProviderConfig = { redirectUri: 'redirectUri' }, oauthType: '2.0', - popupOptions: {} + authContextOptions: {} } export default class OAuth2 { @@ -46,10 +46,10 @@ export default class OAuth2 { let url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?') - this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions) + this.oauthContext = new OAuthContext(url, this.providerConfig.name, this.providerConfig.authContextOptions) return new Promise((resolve, reject) => { - this.oauthPopup.open(this.providerConfig.redirectUri).then((response) => { + this.oauthContext.open(this.providerConfig.redirectUri).then((response) => { if (this.providerConfig.responseType === 'token' || !this.providerConfig.url) { return resolve(response) } @@ -69,7 +69,7 @@ export default class OAuth2 { * Exchange temporary oauth data for access token * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {[type]} oauth [description] * @param {[type]} userData [description] * @return {[type]} [description] @@ -115,7 +115,7 @@ export default class OAuth2 { * Stringify oauth params * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @return {String} */ _stringifyRequestParams() { @@ -151,4 +151,4 @@ export default class OAuth2 { return param.join('=') }).join('&') } -} \ No newline at end of file +} diff --git a/src/oauth/popup.js b/src/oauth/popup.js deleted file mode 100644 index e75fcf4..0000000 --- a/src/oauth/popup.js +++ /dev/null @@ -1,88 +0,0 @@ -import Promise from '../promise.js' -import { objectExtend, parseQueryString, getFullUrlPath, isUndefined } from '../utils.js' - -/** - * OAuth2 popup management class - * - * @author Sahat Yalkabov - * @copyright Class mostly taken from https://github.com/sahat/satellizer - * and adjusted to fit vue-authenticate library - */ -export default class OAuthPopup { - constructor(url, name, popupOptions) { - this.popup = null - this.url = url - this.name = name - this.popupOptions = popupOptions - } - - open(redirectUri, skipPooling) { - try { - this.popup = window.open(this.url, this.name, this._stringifyOptions()) - if (this.popup && this.popup.focus) { - this.popup.focus() - } - - if (skipPooling) { - return Promise.resolve() - } else { - return this.pooling(redirectUri) - } - } catch(e) { - return Promise.reject(new Error('OAuth popup error occurred')) - } - } - - pooling(redirectUri) { - return new Promise((resolve, reject) => { - const redirectUriParser = document.createElement('a') - redirectUriParser.href = redirectUri - const redirectUriPath = getFullUrlPath(redirectUriParser) - - let poolingInterval = setInterval(() => { - if (!this.popup || this.popup.closed || this.popup.closed === undefined) { - clearInterval(poolingInterval) - poolingInterval = null - reject(new Error('Auth popup window closed')) - } - - try { - const popupWindowPath = getFullUrlPath(this.popup.location) - - if (popupWindowPath === redirectUriPath) { - if (this.popup.location.search || this.popup.location.hash) { - const query = parseQueryString(this.popup.location.search.substring(1).replace(/\/$/, '')); - const hash = parseQueryString(this.popup.location.hash.substring(1).replace(/[\/$]/, '')); - let params = objectExtend({}, query); - params = objectExtend(params, hash) - - if (params.error) { - reject(new Error(params.error)); - } else { - resolve(params); - } - } else { - reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')) - } - - clearInterval(poolingInterval) - poolingInterval = null - this.popup.close() - } - } catch(e) { - // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. - } - }, 250) - }) - } - - _stringifyOptions() { - let options = [] - for (var optionKey in this.popupOptions) { - if (!isUndefined(this.popupOptions[optionKey])) { - options.push(`${optionKey}=${this.popupOptions[optionKey]}`) - } - } - return options.join(',') - } -} \ No newline at end of file diff --git a/src/options.js b/src/options.js index a5d34bb..5f54eaa 100644 --- a/src/options.js +++ b/src/options.js @@ -70,7 +70,7 @@ export default { scopeDelimiter: ',', display: 'popup', oauthType: '2.0', - popupOptions: { width: 580, height: 400 } + authContextOptions: { width: 580, height: 400 } }, google: { @@ -85,7 +85,7 @@ export default { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 452, height: 633 } + authContextOptions: { width: 452, height: 633 } }, github: { @@ -97,7 +97,7 @@ export default { scope: ['user:email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, instagram: { @@ -109,7 +109,7 @@ export default { scope: ['basic'], scopeDelimiter: '+', oauthType: '2.0', - popupOptions: { width: null, height: null } + authContextOptions: { width: null, height: null } }, twitter: { @@ -118,7 +118,7 @@ export default { authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: { width: 495, height: 645 } + authContextOptions: { width: 495, height: 645 } }, bitbucket: { @@ -130,7 +130,7 @@ export default { scope: ['email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, linkedin: { @@ -143,7 +143,7 @@ export default { scopeDelimiter: ' ', state: 'STATE', oauthType: '2.0', - popupOptions: { width: 527, height: 582 } + authContextOptions: { width: 527, height: 582 } }, live: { @@ -156,7 +156,7 @@ export default { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 500, height: 560 } + authContextOptions: { width: 500, height: 560 } }, oauth1: { @@ -165,7 +165,7 @@ export default { authorizationEndpoint: null, redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: null + authContextOptions: null }, oauth2: { @@ -182,7 +182,7 @@ export default { scopeDelimiter: null, state: null, oauthType: '2.0', - popupOptions: null, + authContextOptions: null, responseType: 'code', responseParams: { code: 'code', From cd54aed0f5c7c0a00a6e654a082f211b7286444b Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 11:36:07 +0200 Subject: [PATCH 02/16] Preparation for npm publish: Updated build output --- dist/vue-authenticate.common.js | 133 ++++++++++++++++++++------------ dist/vue-authenticate.es2015.js | 133 ++++++++++++++++++++------------ dist/vue-authenticate.js | 133 ++++++++++++++++++++------------ dist/vue-authenticate.min.js | 2 +- example/vue-authenticate.js | 133 ++++++++++++++++++++------------ 5 files changed, 333 insertions(+), 201 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index 02c2cf9..e3df87a 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -550,7 +550,7 @@ var defaultOptions = { scopeDelimiter: ',', display: 'popup', oauthType: '2.0', - popupOptions: { width: 580, height: 400 } + authContextOptions: { width: 580, height: 400 } }, google: { @@ -565,7 +565,7 @@ var defaultOptions = { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 452, height: 633 } + authContextOptions: { width: 452, height: 633 } }, github: { @@ -577,7 +577,7 @@ var defaultOptions = { scope: ['user:email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, instagram: { @@ -589,7 +589,7 @@ var defaultOptions = { scope: ['basic'], scopeDelimiter: '+', oauthType: '2.0', - popupOptions: { width: null, height: null } + authContextOptions: { width: null, height: null } }, twitter: { @@ -598,7 +598,7 @@ var defaultOptions = { authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: { width: 495, height: 645 } + authContextOptions: { width: 495, height: 645 } }, bitbucket: { @@ -610,7 +610,7 @@ var defaultOptions = { scope: ['email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, linkedin: { @@ -623,7 +623,7 @@ var defaultOptions = { scopeDelimiter: ' ', state: 'STATE', oauthType: '2.0', - popupOptions: { width: 527, height: 582 } + authContextOptions: { width: 527, height: 582 } }, live: { @@ -636,7 +636,7 @@ var defaultOptions = { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 500, height: 560 } + authContextOptions: { width: 500, height: 560 } }, oauth1: { @@ -645,7 +645,7 @@ var defaultOptions = { authorizationEndpoint: null, redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: null + authContextOptions: null }, oauth2: { @@ -662,7 +662,7 @@ var defaultOptions = { scopeDelimiter: null, state: null, oauthType: '2.0', - popupOptions: null, + authContextOptions: null, responseType: 'code', responseParams: { code: 'code', @@ -816,24 +816,35 @@ function StorageFactory(options) { } /** - * OAuth2 popup management class - * + * OAuth2 popup/iframe management class + * * @author Sahat Yalkabov - * @copyright Class mostly taken from https://github.com/sahat/satellizer + * @copyright Class mostly taken from https://github.com/sahat/satellizer/blob/master/src/popup.ts * and adjusted to fit vue-authenticate library */ -var OAuthPopup = function OAuthPopup(url, name, popupOptions) { - this.popup = null; +var OAuthContext = function OAuthContext (url, name, authContextOptions) { + if ( authContextOptions === void 0 ) authContextOptions = {}; + + this.authWindow = null; this.url = url; this.name = name; - this.popupOptions = popupOptions; + this.authContextOptions = authContextOptions; }; -OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { +var prototypeAccessors = { iframeTarget: {} }; + +OAuthContext.prototype.open = function open (redirectUri, skipPooling) { try { - this.popup = window.open(this.url, this.name, this._stringifyOptions()); - if (this.popup && this.popup.focus) { - this.popup.focus(); + if (this.authContextOptions.iframe) { + this.iframe = document.createElement('iframe'); + this.iframe.src = this.url; + this.iframeTarget.appendChild(this.iframe); + this.authWindow = this.iframe.contentWindow; + } else { + this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); + if (this.authWindow && this.authWindow.focus) { + this.authWindow.focus(); + } } if (skipPooling) { @@ -841,12 +852,26 @@ OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { } else { return this.pooling(redirectUri) } - } catch(e) { - return Promise$1.reject(new Error('OAuth popup error occurred')) + } catch (e) { + return Promise$1.reject(new Error('Error occurred while opening authentication context')) + } +}; + +prototypeAccessors.iframeTarget.get = function () { + if (!this._iframeTarget) { + if (this.authContextOptions.iframeTarget) { + this._iframeTarget = this.authContextOptions.iframeTarget; + } else { + this._iframeTarget = document.createElement('div'); + this._iframeTarget.style.display = 'none'; + document.body.appendChild(this._iframeTarget); + } } + + return this._iframeTarget }; -OAuthPopup.prototype.pooling = function pooling (redirectUri) { +OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; return new Promise$1(function (resolve, reject) { @@ -855,19 +880,21 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { - clearInterval(poolingInterval); - poolingInterval = null; - reject(new Error('Auth popup window closed')); + if (!this$1.authContextOptions.iframe) { + if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('Auth popup window closed')); + } } try { - var popupWindowPath = getFullUrlPath(this$1.popup.location); + var authWindowPath = getFullUrlPath(this$1.authWindow.location); - if (popupWindowPath === redirectUriPath) { - if (this$1.popup.location.search || this$1.popup.location.hash) { - var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindowPath === redirectUriPath) { + if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { + var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -882,27 +909,33 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; - this$1.popup.close(); + if (this$1.authContextOptions.iframe) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + this$1.authWindow.close(); + } } - } catch(e) { + } catch (e) { // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) }; -OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { +OAuthContext.prototype._stringifyOptions = function _stringifyOptions () { var this$1 = this; var options = []; - for (var optionKey in this$1.popupOptions) { - if (!isUndefined(this$1.popupOptions[optionKey])) { - options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); + for (var optionKey in this$1.authContextOptions) { + if (!isUndefined(this$1.authContextOptions[optionKey])) { + options.push((optionKey + "=" + (this$1.authContextOptions[optionKey]))); } } return options.join(',') }; +Object.defineProperties( OAuthContext.prototype, prototypeAccessors ); + var defaultProviderConfig = { name: null, url: null, @@ -914,7 +947,7 @@ var defaultProviderConfig = { requiredUrlParams: null, defaultUrlParams: null, oauthType: '1.0', - popupOptions: {} + authContextOptions: {} }; var OAuth = function OAuth($http, storage, providerConfig, options) { @@ -926,17 +959,17 @@ var OAuth = function OAuth($http, storage, providerConfig, options) { }; /** - * Initialize OAuth1 process + * Initialize OAuth1 process * @param{Object} userData User data * @return {Promise} */ OAuth.prototype.init = function init (userData) { var this$1 = this; - this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext('about:blank', this.providerConfig.name, this.providerConfig.authContextOptions); if (window && !window['cordova']) { - this.oauthPopup.open(this.providerConfig.redirectUri, true); + this.oauthContext.open(this.providerConfig.redirectUri, true); } return this.getRequestToken().then(function (response) { @@ -972,11 +1005,11 @@ OAuth.prototype.getRequestToken = function getRequestToken () { OAuth.prototype.openPopup = function openPopup (response) { var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); - this.oauthPopup.popup.location = url; + this.oauthContext.popup.location = url; if (window && window['cordova']) { - return this.oauthPopup.open(this.providerConfig.redirectUri) + return this.oauthContext.open(this.providerConfig.redirectUri) } else { - return this.oauthPopup.pooling(this.providerConfig.redirectUri) + return this.oauthContext.pooling(this.providerConfig.redirectUri) } }; @@ -1033,7 +1066,7 @@ var defaultProviderConfig$1 = { redirectUri: 'redirectUri' }, oauthType: '2.0', - popupOptions: {} + authContextOptions: {} }; var OAuth2 = function OAuth2($http, storage, providerConfig, options) { @@ -1056,10 +1089,10 @@ OAuth2.prototype.init = function init (userData) { var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); - this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext(url, this.providerConfig.name, this.providerConfig.authContextOptions); return new Promise(function (resolve, reject) { - this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { + this$1.oauthContext.open(this$1.providerConfig.redirectUri).then(function (response) { if (this$1.providerConfig.responseType === 'token' || !this$1.providerConfig.url) { return resolve(response) } @@ -1079,7 +1112,7 @@ OAuth2.prototype.init = function init (userData) { * Exchange temporary oauth data for access token * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param{[type]} oauth [description] * @param{[type]} userData [description] * @return {[type]} [description] @@ -1127,7 +1160,7 @@ OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) * Stringify oauth params * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @return {String} */ OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index a0ca007..a7d82ac 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -548,7 +548,7 @@ var defaultOptions = { scopeDelimiter: ',', display: 'popup', oauthType: '2.0', - popupOptions: { width: 580, height: 400 } + authContextOptions: { width: 580, height: 400 } }, google: { @@ -563,7 +563,7 @@ var defaultOptions = { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 452, height: 633 } + authContextOptions: { width: 452, height: 633 } }, github: { @@ -575,7 +575,7 @@ var defaultOptions = { scope: ['user:email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, instagram: { @@ -587,7 +587,7 @@ var defaultOptions = { scope: ['basic'], scopeDelimiter: '+', oauthType: '2.0', - popupOptions: { width: null, height: null } + authContextOptions: { width: null, height: null } }, twitter: { @@ -596,7 +596,7 @@ var defaultOptions = { authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: { width: 495, height: 645 } + authContextOptions: { width: 495, height: 645 } }, bitbucket: { @@ -608,7 +608,7 @@ var defaultOptions = { scope: ['email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, linkedin: { @@ -621,7 +621,7 @@ var defaultOptions = { scopeDelimiter: ' ', state: 'STATE', oauthType: '2.0', - popupOptions: { width: 527, height: 582 } + authContextOptions: { width: 527, height: 582 } }, live: { @@ -634,7 +634,7 @@ var defaultOptions = { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 500, height: 560 } + authContextOptions: { width: 500, height: 560 } }, oauth1: { @@ -643,7 +643,7 @@ var defaultOptions = { authorizationEndpoint: null, redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: null + authContextOptions: null }, oauth2: { @@ -660,7 +660,7 @@ var defaultOptions = { scopeDelimiter: null, state: null, oauthType: '2.0', - popupOptions: null, + authContextOptions: null, responseType: 'code', responseParams: { code: 'code', @@ -814,24 +814,35 @@ function StorageFactory(options) { } /** - * OAuth2 popup management class - * + * OAuth2 popup/iframe management class + * * @author Sahat Yalkabov - * @copyright Class mostly taken from https://github.com/sahat/satellizer + * @copyright Class mostly taken from https://github.com/sahat/satellizer/blob/master/src/popup.ts * and adjusted to fit vue-authenticate library */ -var OAuthPopup = function OAuthPopup(url, name, popupOptions) { - this.popup = null; +var OAuthContext = function OAuthContext (url, name, authContextOptions) { + if ( authContextOptions === void 0 ) authContextOptions = {}; + + this.authWindow = null; this.url = url; this.name = name; - this.popupOptions = popupOptions; + this.authContextOptions = authContextOptions; }; -OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { +var prototypeAccessors = { iframeTarget: {} }; + +OAuthContext.prototype.open = function open (redirectUri, skipPooling) { try { - this.popup = window.open(this.url, this.name, this._stringifyOptions()); - if (this.popup && this.popup.focus) { - this.popup.focus(); + if (this.authContextOptions.iframe) { + this.iframe = document.createElement('iframe'); + this.iframe.src = this.url; + this.iframeTarget.appendChild(this.iframe); + this.authWindow = this.iframe.contentWindow; + } else { + this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); + if (this.authWindow && this.authWindow.focus) { + this.authWindow.focus(); + } } if (skipPooling) { @@ -839,12 +850,26 @@ OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { } else { return this.pooling(redirectUri) } - } catch(e) { - return Promise$1.reject(new Error('OAuth popup error occurred')) + } catch (e) { + return Promise$1.reject(new Error('Error occurred while opening authentication context')) + } +}; + +prototypeAccessors.iframeTarget.get = function () { + if (!this._iframeTarget) { + if (this.authContextOptions.iframeTarget) { + this._iframeTarget = this.authContextOptions.iframeTarget; + } else { + this._iframeTarget = document.createElement('div'); + this._iframeTarget.style.display = 'none'; + document.body.appendChild(this._iframeTarget); + } } + + return this._iframeTarget }; -OAuthPopup.prototype.pooling = function pooling (redirectUri) { +OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; return new Promise$1(function (resolve, reject) { @@ -853,19 +878,21 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { - clearInterval(poolingInterval); - poolingInterval = null; - reject(new Error('Auth popup window closed')); + if (!this$1.authContextOptions.iframe) { + if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('Auth popup window closed')); + } } try { - var popupWindowPath = getFullUrlPath(this$1.popup.location); + var authWindowPath = getFullUrlPath(this$1.authWindow.location); - if (popupWindowPath === redirectUriPath) { - if (this$1.popup.location.search || this$1.popup.location.hash) { - var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindowPath === redirectUriPath) { + if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { + var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -880,27 +907,33 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; - this$1.popup.close(); + if (this$1.authContextOptions.iframe) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + this$1.authWindow.close(); + } } - } catch(e) { + } catch (e) { // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) }; -OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { +OAuthContext.prototype._stringifyOptions = function _stringifyOptions () { var this$1 = this; var options = []; - for (var optionKey in this$1.popupOptions) { - if (!isUndefined(this$1.popupOptions[optionKey])) { - options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); + for (var optionKey in this$1.authContextOptions) { + if (!isUndefined(this$1.authContextOptions[optionKey])) { + options.push((optionKey + "=" + (this$1.authContextOptions[optionKey]))); } } return options.join(',') }; +Object.defineProperties( OAuthContext.prototype, prototypeAccessors ); + var defaultProviderConfig = { name: null, url: null, @@ -912,7 +945,7 @@ var defaultProviderConfig = { requiredUrlParams: null, defaultUrlParams: null, oauthType: '1.0', - popupOptions: {} + authContextOptions: {} }; var OAuth = function OAuth($http, storage, providerConfig, options) { @@ -924,17 +957,17 @@ var OAuth = function OAuth($http, storage, providerConfig, options) { }; /** - * Initialize OAuth1 process + * Initialize OAuth1 process * @param{Object} userData User data * @return {Promise} */ OAuth.prototype.init = function init (userData) { var this$1 = this; - this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext('about:blank', this.providerConfig.name, this.providerConfig.authContextOptions); if (window && !window['cordova']) { - this.oauthPopup.open(this.providerConfig.redirectUri, true); + this.oauthContext.open(this.providerConfig.redirectUri, true); } return this.getRequestToken().then(function (response) { @@ -970,11 +1003,11 @@ OAuth.prototype.getRequestToken = function getRequestToken () { OAuth.prototype.openPopup = function openPopup (response) { var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); - this.oauthPopup.popup.location = url; + this.oauthContext.popup.location = url; if (window && window['cordova']) { - return this.oauthPopup.open(this.providerConfig.redirectUri) + return this.oauthContext.open(this.providerConfig.redirectUri) } else { - return this.oauthPopup.pooling(this.providerConfig.redirectUri) + return this.oauthContext.pooling(this.providerConfig.redirectUri) } }; @@ -1031,7 +1064,7 @@ var defaultProviderConfig$1 = { redirectUri: 'redirectUri' }, oauthType: '2.0', - popupOptions: {} + authContextOptions: {} }; var OAuth2 = function OAuth2($http, storage, providerConfig, options) { @@ -1054,10 +1087,10 @@ OAuth2.prototype.init = function init (userData) { var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); - this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext(url, this.providerConfig.name, this.providerConfig.authContextOptions); return new Promise(function (resolve, reject) { - this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { + this$1.oauthContext.open(this$1.providerConfig.redirectUri).then(function (response) { if (this$1.providerConfig.responseType === 'token' || !this$1.providerConfig.url) { return resolve(response) } @@ -1077,7 +1110,7 @@ OAuth2.prototype.init = function init (userData) { * Exchange temporary oauth data for access token * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param{[type]} oauth [description] * @param{[type]} userData [description] * @return {[type]} [description] @@ -1125,7 +1158,7 @@ OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) * Stringify oauth params * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @return {String} */ OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index bddb965..f6d8009 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -554,7 +554,7 @@ var defaultOptions = { scopeDelimiter: ',', display: 'popup', oauthType: '2.0', - popupOptions: { width: 580, height: 400 } + authContextOptions: { width: 580, height: 400 } }, google: { @@ -569,7 +569,7 @@ var defaultOptions = { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 452, height: 633 } + authContextOptions: { width: 452, height: 633 } }, github: { @@ -581,7 +581,7 @@ var defaultOptions = { scope: ['user:email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, instagram: { @@ -593,7 +593,7 @@ var defaultOptions = { scope: ['basic'], scopeDelimiter: '+', oauthType: '2.0', - popupOptions: { width: null, height: null } + authContextOptions: { width: null, height: null } }, twitter: { @@ -602,7 +602,7 @@ var defaultOptions = { authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: { width: 495, height: 645 } + authContextOptions: { width: 495, height: 645 } }, bitbucket: { @@ -614,7 +614,7 @@ var defaultOptions = { scope: ['email'], scopeDelimiter: ' ', oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } + authContextOptions: { width: 1020, height: 618 } }, linkedin: { @@ -627,7 +627,7 @@ var defaultOptions = { scopeDelimiter: ' ', state: 'STATE', oauthType: '2.0', - popupOptions: { width: 527, height: 582 } + authContextOptions: { width: 527, height: 582 } }, live: { @@ -640,7 +640,7 @@ var defaultOptions = { scopeDelimiter: ' ', display: 'popup', oauthType: '2.0', - popupOptions: { width: 500, height: 560 } + authContextOptions: { width: 500, height: 560 } }, oauth1: { @@ -649,7 +649,7 @@ var defaultOptions = { authorizationEndpoint: null, redirectUri: getRedirectUri(), oauthType: '1.0', - popupOptions: null + authContextOptions: null }, oauth2: { @@ -666,7 +666,7 @@ var defaultOptions = { scopeDelimiter: null, state: null, oauthType: '2.0', - popupOptions: null, + authContextOptions: null, responseType: 'code', responseParams: { code: 'code', @@ -820,24 +820,35 @@ function StorageFactory(options) { } /** - * OAuth2 popup management class - * + * OAuth2 popup/iframe management class + * * @author Sahat Yalkabov - * @copyright Class mostly taken from https://github.com/sahat/satellizer + * @copyright Class mostly taken from https://github.com/sahat/satellizer/blob/master/src/popup.ts * and adjusted to fit vue-authenticate library */ -var OAuthPopup = function OAuthPopup(url, name, popupOptions) { - this.popup = null; +var OAuthContext = function OAuthContext (url, name, authContextOptions) { + if ( authContextOptions === void 0 ) authContextOptions = {}; + + this.authWindow = null; this.url = url; this.name = name; - this.popupOptions = popupOptions; + this.authContextOptions = authContextOptions; }; -OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { +var prototypeAccessors = { iframeTarget: {} }; + +OAuthContext.prototype.open = function open (redirectUri, skipPooling) { try { - this.popup = window.open(this.url, this.name, this._stringifyOptions()); - if (this.popup && this.popup.focus) { - this.popup.focus(); + if (this.authContextOptions.iframe) { + this.iframe = document.createElement('iframe'); + this.iframe.src = this.url; + this.iframeTarget.appendChild(this.iframe); + this.authWindow = this.iframe.contentWindow; + } else { + this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); + if (this.authWindow && this.authWindow.focus) { + this.authWindow.focus(); + } } if (skipPooling) { @@ -845,12 +856,26 @@ OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { } else { return this.pooling(redirectUri) } - } catch(e) { - return Promise$1.reject(new Error('OAuth popup error occurred')) + } catch (e) { + return Promise$1.reject(new Error('Error occurred while opening authentication context')) + } +}; + +prototypeAccessors.iframeTarget.get = function () { + if (!this._iframeTarget) { + if (this.authContextOptions.iframeTarget) { + this._iframeTarget = this.authContextOptions.iframeTarget; + } else { + this._iframeTarget = document.createElement('div'); + this._iframeTarget.style.display = 'none'; + document.body.appendChild(this._iframeTarget); + } } + + return this._iframeTarget }; -OAuthPopup.prototype.pooling = function pooling (redirectUri) { +OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; return new Promise$1(function (resolve, reject) { @@ -859,19 +884,21 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { - clearInterval(poolingInterval); - poolingInterval = null; - reject(new Error('Auth popup window closed')); + if (!this$1.authContextOptions.iframe) { + if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('Auth popup window closed')); + } } try { - var popupWindowPath = getFullUrlPath(this$1.popup.location); + var authWindowPath = getFullUrlPath(this$1.authWindow.location); - if (popupWindowPath === redirectUriPath) { - if (this$1.popup.location.search || this$1.popup.location.hash) { - var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindowPath === redirectUriPath) { + if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { + var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -886,27 +913,33 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; - this$1.popup.close(); + if (this$1.authContextOptions.iframe) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + this$1.authWindow.close(); + } } - } catch(e) { + } catch (e) { // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) }; -OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { +OAuthContext.prototype._stringifyOptions = function _stringifyOptions () { var this$1 = this; var options = []; - for (var optionKey in this$1.popupOptions) { - if (!isUndefined(this$1.popupOptions[optionKey])) { - options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); + for (var optionKey in this$1.authContextOptions) { + if (!isUndefined(this$1.authContextOptions[optionKey])) { + options.push((optionKey + "=" + (this$1.authContextOptions[optionKey]))); } } return options.join(',') }; +Object.defineProperties( OAuthContext.prototype, prototypeAccessors ); + var defaultProviderConfig = { name: null, url: null, @@ -918,7 +951,7 @@ var defaultProviderConfig = { requiredUrlParams: null, defaultUrlParams: null, oauthType: '1.0', - popupOptions: {} + authContextOptions: {} }; var OAuth = function OAuth($http, storage, providerConfig, options) { @@ -930,17 +963,17 @@ var OAuth = function OAuth($http, storage, providerConfig, options) { }; /** - * Initialize OAuth1 process + * Initialize OAuth1 process * @param{Object} userData User data * @return {Promise} */ OAuth.prototype.init = function init (userData) { var this$1 = this; - this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext('about:blank', this.providerConfig.name, this.providerConfig.authContextOptions); if (window && !window['cordova']) { - this.oauthPopup.open(this.providerConfig.redirectUri, true); + this.oauthContext.open(this.providerConfig.redirectUri, true); } return this.getRequestToken().then(function (response) { @@ -976,11 +1009,11 @@ OAuth.prototype.getRequestToken = function getRequestToken () { OAuth.prototype.openPopup = function openPopup (response) { var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); - this.oauthPopup.popup.location = url; + this.oauthContext.popup.location = url; if (window && window['cordova']) { - return this.oauthPopup.open(this.providerConfig.redirectUri) + return this.oauthContext.open(this.providerConfig.redirectUri) } else { - return this.oauthPopup.pooling(this.providerConfig.redirectUri) + return this.oauthContext.pooling(this.providerConfig.redirectUri) } }; @@ -1037,7 +1070,7 @@ var defaultProviderConfig$1 = { redirectUri: 'redirectUri' }, oauthType: '2.0', - popupOptions: {} + authContextOptions: {} }; var OAuth2 = function OAuth2($http, storage, providerConfig, options) { @@ -1060,10 +1093,10 @@ OAuth2.prototype.init = function init (userData) { var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); - this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext(url, this.providerConfig.name, this.providerConfig.authContextOptions); return new Promise(function (resolve, reject) { - this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { + this$1.oauthContext.open(this$1.providerConfig.redirectUri).then(function (response) { if (this$1.providerConfig.responseType === 'token' || !this$1.providerConfig.url) { return resolve(response) } @@ -1083,7 +1116,7 @@ OAuth2.prototype.init = function init (userData) { * Exchange temporary oauth data for access token * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param{[type]} oauth [description] * @param{[type]} userData [description] * @return {[type]} [description] @@ -1131,7 +1164,7 @@ OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) * Stringify oauth params * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @return {String} */ OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index ef6d989..ff658b0 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -4,4 +4,4 @@ * Released under the MIT License. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueAuthenticate=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return null!==t&&"object"==typeof t}function n(t){return"string"==typeof t}function r(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function a(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function s(t){var e="https:"===t.protocol;return t.protocol+"//"+t.hostname+":"+(t.port||(e?"443":"80"))+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function u(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function p(t){var e;if("undefined"!=typeof module&&module.exports)try{e=require("buffer").Buffer}catch(t){}var o=String.fromCharCode,n=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g"),r=function(t){switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),n=e-65536;return o(55296+(n>>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function c(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function h(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),h(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function g(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function m(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,g._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof g)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&g._immediateFn(function(){t._handled||g._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function g(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function m(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,g._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof g)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&g._immediateFn(function(){t._handled||g._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e - * @copyright Class mostly taken from https://github.com/sahat/satellizer + * @copyright Class mostly taken from https://github.com/sahat/satellizer/blob/master/src/popup.ts * and adjusted to fit vue-authenticate library */ -var OAuthPopup = function OAuthPopup(url, name, popupOptions) { - this.popup = null; +var OAuthContext = function OAuthContext (url, name, authContextOptions) { + if ( authContextOptions === void 0 ) authContextOptions = {}; + + this.authWindow = null; this.url = url; this.name = name; - this.popupOptions = popupOptions; + this.authContextOptions = authContextOptions; }; -OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { +var prototypeAccessors = { iframeTarget: {} }; + +OAuthContext.prototype.open = function open (redirectUri, skipPooling) { try { - this.popup = window.open(this.url, this.name, this._stringifyOptions()); - if (this.popup && this.popup.focus) { - this.popup.focus(); + if (this.authContextOptions.iframe) { + this.iframe = document.createElement('iframe'); + this.iframe.src = this.url; + this.iframeTarget.appendChild(this.iframe); + this.authWindow = this.iframe.contentWindow; + } else { + this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); + if (this.authWindow && this.authWindow.focus) { + this.authWindow.focus(); + } } if (skipPooling) { @@ -845,12 +856,26 @@ OAuthPopup.prototype.open = function open (redirectUri, skipPooling) { } else { return this.pooling(redirectUri) } - } catch(e) { - return Promise$1.reject(new Error('OAuth popup error occurred')) + } catch (e) { + return Promise$1.reject(new Error('Error occurred while opening authentication context')) + } +}; + +prototypeAccessors.iframeTarget.get = function () { + if (!this._iframeTarget) { + if (this.authContextOptions.iframeTarget) { + this._iframeTarget = this.authContextOptions.iframeTarget; + } else { + this._iframeTarget = document.createElement('div'); + this._iframeTarget.style.display = 'none'; + document.body.appendChild(this._iframeTarget); + } } + + return this._iframeTarget }; -OAuthPopup.prototype.pooling = function pooling (redirectUri) { +OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; return new Promise$1(function (resolve, reject) { @@ -859,19 +884,21 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.popup || this$1.popup.closed || this$1.popup.closed === undefined) { - clearInterval(poolingInterval); - poolingInterval = null; - reject(new Error('Auth popup window closed')); + if (!this$1.authContextOptions.iframe) { + if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('Auth popup window closed')); + } } try { - var popupWindowPath = getFullUrlPath(this$1.popup.location); + var authWindowPath = getFullUrlPath(this$1.authWindow.location); - if (popupWindowPath === redirectUriPath) { - if (this$1.popup.location.search || this$1.popup.location.hash) { - var query = parseQueryString(this$1.popup.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.popup.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindowPath === redirectUriPath) { + if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { + var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -886,27 +913,33 @@ OAuthPopup.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; - this$1.popup.close(); + if (this$1.authContextOptions.iframe) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + this$1.authWindow.close(); + } } - } catch(e) { + } catch (e) { // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) }; -OAuthPopup.prototype._stringifyOptions = function _stringifyOptions () { +OAuthContext.prototype._stringifyOptions = function _stringifyOptions () { var this$1 = this; var options = []; - for (var optionKey in this$1.popupOptions) { - if (!isUndefined(this$1.popupOptions[optionKey])) { - options.push((optionKey + "=" + (this$1.popupOptions[optionKey]))); + for (var optionKey in this$1.authContextOptions) { + if (!isUndefined(this$1.authContextOptions[optionKey])) { + options.push((optionKey + "=" + (this$1.authContextOptions[optionKey]))); } } return options.join(',') }; +Object.defineProperties( OAuthContext.prototype, prototypeAccessors ); + var defaultProviderConfig = { name: null, url: null, @@ -918,7 +951,7 @@ var defaultProviderConfig = { requiredUrlParams: null, defaultUrlParams: null, oauthType: '1.0', - popupOptions: {} + authContextOptions: {} }; var OAuth = function OAuth($http, storage, providerConfig, options) { @@ -930,17 +963,17 @@ var OAuth = function OAuth($http, storage, providerConfig, options) { }; /** - * Initialize OAuth1 process + * Initialize OAuth1 process * @param{Object} userData User data * @return {Promise} */ OAuth.prototype.init = function init (userData) { var this$1 = this; - this.oauthPopup = new OAuthPopup('about:blank', this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext('about:blank', this.providerConfig.name, this.providerConfig.authContextOptions); if (window && !window['cordova']) { - this.oauthPopup.open(this.providerConfig.redirectUri, true); + this.oauthContext.open(this.providerConfig.redirectUri, true); } return this.getRequestToken().then(function (response) { @@ -976,11 +1009,11 @@ OAuth.prototype.getRequestToken = function getRequestToken () { OAuth.prototype.openPopup = function openPopup (response) { var url = [this.providerConfig.authorizationEndpoint, this.buildQueryString(response[this.options.responseDataKey])].join('?'); - this.oauthPopup.popup.location = url; + this.oauthContext.popup.location = url; if (window && window['cordova']) { - return this.oauthPopup.open(this.providerConfig.redirectUri) + return this.oauthContext.open(this.providerConfig.redirectUri) } else { - return this.oauthPopup.pooling(this.providerConfig.redirectUri) + return this.oauthContext.pooling(this.providerConfig.redirectUri) } }; @@ -1037,7 +1070,7 @@ var defaultProviderConfig$1 = { redirectUri: 'redirectUri' }, oauthType: '2.0', - popupOptions: {} + authContextOptions: {} }; var OAuth2 = function OAuth2($http, storage, providerConfig, options) { @@ -1060,10 +1093,10 @@ OAuth2.prototype.init = function init (userData) { var url = [this.providerConfig.authorizationEndpoint, this._stringifyRequestParams()].join('?'); - this.oauthPopup = new OAuthPopup(url, this.providerConfig.name, this.providerConfig.popupOptions); + this.oauthContext = new OAuthContext(url, this.providerConfig.name, this.providerConfig.authContextOptions); return new Promise(function (resolve, reject) { - this$1.oauthPopup.open(this$1.providerConfig.redirectUri).then(function (response) { + this$1.oauthContext.open(this$1.providerConfig.redirectUri).then(function (response) { if (this$1.providerConfig.responseType === 'token' || !this$1.providerConfig.url) { return resolve(response) } @@ -1083,7 +1116,7 @@ OAuth2.prototype.init = function init (userData) { * Exchange temporary oauth data for access token * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param{[type]} oauth [description] * @param{[type]} userData [description] * @return {[type]} [description] @@ -1131,7 +1164,7 @@ OAuth2.prototype.exchangeForToken = function exchangeForToken (oauth, userData) * Stringify oauth params * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @return {String} */ OAuth2.prototype._stringifyRequestParams = function _stringifyRequestParams () { From 62d6d9d876aace9722bca04422b6da73eb97a324 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 12:16:25 +0200 Subject: [PATCH 03/16] Preparation for npm publish: Updated package.json and README --- README.md | 2 ++ package.json | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 64eafaf..8a7ca9b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ The best part about this library is that it is not strictly coupled to one reque For now it is tested to work with [vue-resource](https://github.com/pagekit/vue-resource) and [axios](https://github.com/mzabriskie/axios) (using [vue-axios](https://github.com/imcvampire/vue-axios) wrapper). +This library supports authentication in popup windows and iframes. + **WARNING:** From version 1.3.0 default request library is `axios` using `vue-axios` wrapper plugin. This library was inspired by well known authentication library for Angular called [Satellizer](https://github.com/sahat/satellizer) developed by [Sahat Yalkabov](http://sahatyalkabov.com). They share almost identical configuration and API so you can easily switch from Angular to Vue.js project. diff --git a/package.json b/package.json index bb767fe..80e6534 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,13 @@ "email": "davor.grubelic@gmail.com", "url": "https://dgrubelic.me" }, + "contributors": [{ + "name": "Balazs Sarvari", + "email": "balazs.sarvari@gmail.com" + }], "repository": { "type": "git", - "url": "https://github.com/dgrubelic/vue-authenticate.git" + "url": "https://github.com/bsarvari/vue-authenticate.git" }, "license": "MIT", "devDependencies": { @@ -61,11 +65,13 @@ }, "tags": [ "auth", + "oauth", "authentication", "login", "registration", "jwt", "token", + "iframe", "vuejs", "vue.js", "github", From 8e912069c65c9d8eadf2545628845de6e7e38b9e Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 13:01:29 +0200 Subject: [PATCH 04/16] Rename module such that it can be published to public npm repo independently of its parent project --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 80e6534..fd0041f 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "vue-authenticate", + "name": "@bsarvari/vue-authenticate", "version": "1.3.5-beta.1", "description": "Authentication library for Vue.js", "main": "dist/vue-authenticate.js", From 0aaf58b56b0773ed13ee17acf5bc128364934f8c Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 17:59:04 +0200 Subject: [PATCH 05/16] Remove iframe target DOM node from document if authContextOptions has no iframeTarget property (to avoid creating many hidden divs in document) --- src/oauth/oauth-context.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/oauth/oauth-context.js b/src/oauth/oauth-context.js index 5a75c9c..deda61a 100644 --- a/src/oauth/oauth-context.js +++ b/src/oauth/oauth-context.js @@ -91,7 +91,11 @@ export default class OAuthContext { clearInterval(poolingInterval) poolingInterval = null if (this.authContextOptions.iframe) { - this.iframeTarget.removeChild(this.iframe) + if (this.authContextOptions.iframeTarget) { + this.iframeTarget.removeChild(this.iframe) + } else { + document.body.removeChild(this.iframeTarget) + } } else { this.authWindow.close() } From 695f725ca7a5b57d999ec39d8f1a256f686ec6ff Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 18:09:29 +0200 Subject: [PATCH 06/16] Dist files --- dist/vue-authenticate.common.js | 6 +++++- dist/vue-authenticate.es2015.js | 6 +++++- dist/vue-authenticate.js | 6 +++++- dist/vue-authenticate.min.js | 2 +- example/vue-authenticate.js | 6 +++++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index e3df87a..133ac48 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -910,7 +910,11 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { - this$1.iframeTarget.removeChild(this$1.iframe); + if (this$1.authContextOptions.iframeTarget) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + document.body.removeChild(this$1.iframeTarget); + } } else { this$1.authWindow.close(); } diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index a7d82ac..41dbee1 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -908,7 +908,11 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { - this$1.iframeTarget.removeChild(this$1.iframe); + if (this$1.authContextOptions.iframeTarget) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + document.body.removeChild(this$1.iframeTarget); + } } else { this$1.authWindow.close(); } diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index f6d8009..7e835c8 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -914,7 +914,11 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { - this$1.iframeTarget.removeChild(this$1.iframe); + if (this$1.authContextOptions.iframeTarget) { + this$1.iframeTarget.removeChild(this$1.iframe); + } else { + document.body.removeChild(this$1.iframeTarget); + } } else { this$1.authWindow.close(); } diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index ff658b0..cc5bbd2 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -4,4 +4,4 @@ * Released under the MIT License. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueAuthenticate=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return null!==t&&"object"==typeof t}function n(t){return"string"==typeof t}function r(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function a(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function s(t){var e="https:"===t.protocol;return t.protocol+"//"+t.hostname+":"+(t.port||(e?"443":"80"))+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function u(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function c(t){var e;if("undefined"!=typeof module&&module.exports)try{e=require("buffer").Buffer}catch(t){}var o=String.fromCharCode,n=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g"),r=function(t){switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),n=e-65536;return o(55296+(n>>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function g(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function m(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,g._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof g)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&g._immediateFn(function(){t._handled||g._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e Date: Tue, 3 Apr 2018 18:10:54 +0200 Subject: [PATCH 07/16] Increase package version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd0041f..c9b8f84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bsarvari/vue-authenticate", - "version": "1.3.5-beta.1", + "version": "1.3.5-beta.1_1", "description": "Authentication library for Vue.js", "main": "dist/vue-authenticate.js", "module": "dist/vue-authenticate.es2015.js", From 2c2346157930c82749b2471015e7c94056414d76 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Tue, 3 Apr 2018 18:16:51 +0200 Subject: [PATCH 08/16] Increase package version + updated dist artifacts --- dist/vue-authenticate.common.js | 2 +- dist/vue-authenticate.es2015.js | 2 +- dist/vue-authenticate.js | 2 +- dist/vue-authenticate.min.js | 2 +- example/vue-authenticate.js | 2 +- package.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index 133ac48..165695d 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1 + * vue-authenticate v1.3.5-beta.1.1 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index 41dbee1..e4b4ea8 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1 + * vue-authenticate v1.3.5-beta.1.1 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index 7e835c8..8262912 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1 + * vue-authenticate v1.3.5-beta.1.1 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index cc5bbd2..ddade42 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1 + * vue-authenticate v1.3.5-beta.1.1 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ diff --git a/example/vue-authenticate.js b/example/vue-authenticate.js index 7e835c8..8262912 100644 --- a/example/vue-authenticate.js +++ b/example/vue-authenticate.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1 + * vue-authenticate v1.3.5-beta.1.1 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ diff --git a/package.json b/package.json index c9b8f84..f46b1f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bsarvari/vue-authenticate", - "version": "1.3.5-beta.1_1", + "version": "1.3.5-beta.1.1", "description": "Authentication library for Vue.js", "main": "dist/vue-authenticate.js", "module": "dist/vue-authenticate.es2015.js", From 31766205bdd7ec4c84de59533ae50eca6a1470d0 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Fri, 13 Apr 2018 16:55:03 +0200 Subject: [PATCH 09/16] A safer way to get full URL path: this fixes the problem on older versions of Safari where location.port is 0 when port is missing from URL (i.e. it's the default 80) --- src/utils.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/utils.js b/src/utils.js index 883999d..039d493 100644 --- a/src/utils.js +++ b/src/utils.js @@ -77,10 +77,10 @@ export function objectExtend(a, b) { /** * Assemble url from two segments - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} baseUrl Base url * @param {String} url URI * @return {String} @@ -102,26 +102,23 @@ export function joinUrl(baseUrl, url) { /** * Get full path based on current location - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {Location} location * @return {String} */ export function getFullUrlPath(location) { - const isHttps = location.protocol === 'https:'; - return location.protocol + '//' + location.hostname + - ':' + (location.port || (isHttps ? '443' : '80')) + - (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); + return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** * Parse query string variables - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} Query string * @return {String} */ @@ -143,7 +140,7 @@ export function parseQueryString(str) { * Decode base64 string * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} str base64 encoded string * @return {Object} */ From 0f46d181f8fbbae922d167de263a5b78f84a9541 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Fri, 13 Apr 2018 16:56:46 +0200 Subject: [PATCH 10/16] Let the authentication timeout if the remote service never redirects to the callback URL --- src/oauth/oauth-context.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/oauth/oauth-context.js b/src/oauth/oauth-context.js index deda61a..0c11054 100644 --- a/src/oauth/oauth-context.js +++ b/src/oauth/oauth-context.js @@ -55,6 +55,14 @@ export default class OAuthContext { } pooling (redirectUri) { + this.timedOut = false + let authTimeout + if (this.authContextOptions.timeout) { + authTimeout = setTimeout(() => { + this.timedOut = true + }, this.authContextOptions.timeout) + } + return new Promise((resolve, reject) => { const redirectUriParser = document.createElement('a') redirectUriParser.href = redirectUri @@ -87,7 +95,7 @@ export default class OAuthContext { } else { reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')) } - + clearTimeout(authTimeout) clearInterval(poolingInterval) poolingInterval = null if (this.authContextOptions.iframe) { @@ -99,9 +107,19 @@ export default class OAuthContext { } else { this.authWindow.close() } + } else { + if (this.timedOut) { + clearInterval(poolingInterval) + poolingInterval = null + reject(new Error('auth_timeout')) + } + } + } catch (e) { // DOMException: Blocked a frame with origin from accessing a cross-origin frame. + if (this.timedOut) { + clearInterval(poolingInterval) + poolingInterval = null + reject(new Error('auth_timeout')) } - } catch (e) { - // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250) }) From 1794e661bd2c33f3b622adcfbef98d52cca4d97f Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Fri, 13 Apr 2018 17:02:19 +0200 Subject: [PATCH 11/16] Increase package version + updated dist artifacts --- dist/vue-authenticate.common.js | 45 ++++++++++++++++++++++----------- dist/vue-authenticate.es2015.js | 45 ++++++++++++++++++++++----------- dist/vue-authenticate.js | 45 ++++++++++++++++++++++----------- dist/vue-authenticate.min.js | 4 +-- example/vue-authenticate.js | 45 ++++++++++++++++++++++----------- package.json | 2 +- 6 files changed, 123 insertions(+), 63 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index 165695d..7d2752f 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.1 + * vue-authenticate v1.3.5-beta.1.2 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -83,10 +83,10 @@ function objectExtend(a, b) { /** * Assemble url from two segments - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} baseUrl Base url * @param {String} url URI * @return {String} @@ -108,26 +108,23 @@ function joinUrl(baseUrl, url) { /** * Get full path based on current location - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {Location} location * @return {String} */ function getFullUrlPath(location) { - var isHttps = location.protocol === 'https:'; - return location.protocol + '//' + location.hostname + - ':' + (location.port || (isHttps ? '443' : '80')) + - (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); + return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** * Parse query string variables - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} Query string * @return {String} */ @@ -149,7 +146,7 @@ function parseQueryString(str) { * Decode base64 string * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} str base64 encoded string * @return {Object} */ @@ -874,6 +871,14 @@ prototypeAccessors.iframeTarget.get = function () { OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; + this.timedOut = false; + var authTimeout; + if (this.authContextOptions.timeout) { + authTimeout = setTimeout(function () { + this$1.timedOut = true; + }, this.authContextOptions.timeout); + } + return new Promise$1(function (resolve, reject) { var redirectUriParser = document.createElement('a'); redirectUriParser.href = redirectUri; @@ -906,7 +911,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); } - + clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { @@ -918,9 +923,19 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { this$1.authWindow.close(); } + } else { + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); + } + } + } catch (e) { // DOMException: Blocked a frame with origin from accessing a cross-origin frame. + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); } - } catch (e) { - // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index e4b4ea8..4c0d47a 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.1 + * vue-authenticate v1.3.5-beta.1.2 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -81,10 +81,10 @@ function objectExtend(a, b) { /** * Assemble url from two segments - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} baseUrl Base url * @param {String} url URI * @return {String} @@ -106,26 +106,23 @@ function joinUrl(baseUrl, url) { /** * Get full path based on current location - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {Location} location * @return {String} */ function getFullUrlPath(location) { - var isHttps = location.protocol === 'https:'; - return location.protocol + '//' + location.hostname + - ':' + (location.port || (isHttps ? '443' : '80')) + - (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); + return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** * Parse query string variables - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} Query string * @return {String} */ @@ -147,7 +144,7 @@ function parseQueryString(str) { * Decode base64 string * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} str base64 encoded string * @return {Object} */ @@ -872,6 +869,14 @@ prototypeAccessors.iframeTarget.get = function () { OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; + this.timedOut = false; + var authTimeout; + if (this.authContextOptions.timeout) { + authTimeout = setTimeout(function () { + this$1.timedOut = true; + }, this.authContextOptions.timeout); + } + return new Promise$1(function (resolve, reject) { var redirectUriParser = document.createElement('a'); redirectUriParser.href = redirectUri; @@ -904,7 +909,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); } - + clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { @@ -916,9 +921,19 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { this$1.authWindow.close(); } + } else { + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); + } + } + } catch (e) { // DOMException: Blocked a frame with origin from accessing a cross-origin frame. + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); } - } catch (e) { - // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index 8262912..462e334 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.1 + * vue-authenticate v1.3.5-beta.1.2 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -87,10 +87,10 @@ function objectExtend(a, b) { /** * Assemble url from two segments - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} baseUrl Base url * @param {String} url URI * @return {String} @@ -112,26 +112,23 @@ function joinUrl(baseUrl, url) { /** * Get full path based on current location - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {Location} location * @return {String} */ function getFullUrlPath(location) { - var isHttps = location.protocol === 'https:'; - return location.protocol + '//' + location.hostname + - ':' + (location.port || (isHttps ? '443' : '80')) + - (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); + return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** * Parse query string variables - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} Query string * @return {String} */ @@ -153,7 +150,7 @@ function parseQueryString(str) { * Decode base64 string * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} str base64 encoded string * @return {Object} */ @@ -878,6 +875,14 @@ prototypeAccessors.iframeTarget.get = function () { OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; + this.timedOut = false; + var authTimeout; + if (this.authContextOptions.timeout) { + authTimeout = setTimeout(function () { + this$1.timedOut = true; + }, this.authContextOptions.timeout); + } + return new Promise$1(function (resolve, reject) { var redirectUriParser = document.createElement('a'); redirectUriParser.href = redirectUri; @@ -910,7 +915,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); } - + clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { @@ -922,9 +927,19 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { this$1.authWindow.close(); } + } else { + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); + } + } + } catch (e) { // DOMException: Blocked a frame with origin from accessing a cross-origin frame. + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); } - } catch (e) { - // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index ddade42..c69a26e 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -1,7 +1,7 @@ /*! - * vue-authenticate v1.3.5-beta.1.1 + * vue-authenticate v1.3.5-beta.1.2 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueAuthenticate=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return null!==t&&"object"==typeof t}function n(t){return"string"==typeof t}function r(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function a(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function s(t){var e="https:"===t.protocol;return t.protocol+"//"+t.hostname+":"+(t.port||(e?"443":"80"))+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function u(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function c(t){var e;if("undefined"!=typeof module&&module.exports)try{e=require("buffer").Buffer}catch(t){}var o=String.fromCharCode,n=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g"),r=function(t){switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),n=e-65536;return o(55296+(n>>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function c(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} baseUrl Base url * @param {String} url URI * @return {String} @@ -112,26 +112,23 @@ function joinUrl(baseUrl, url) { /** * Get full path based on current location - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {Location} location * @return {String} */ function getFullUrlPath(location) { - var isHttps = location.protocol === 'https:'; - return location.protocol + '//' + location.hostname + - ':' + (location.port || (isHttps ? '443' : '80')) + - (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname); + return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** * Parse query string variables - * + * * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} Query string * @return {String} */ @@ -153,7 +150,7 @@ function parseQueryString(str) { * Decode base64 string * @author Sahat Yalkabov * @copyright Method taken from https://github.com/sahat/satellizer - * + * * @param {String} str base64 encoded string * @return {Object} */ @@ -878,6 +875,14 @@ prototypeAccessors.iframeTarget.get = function () { OAuthContext.prototype.pooling = function pooling (redirectUri) { var this$1 = this; + this.timedOut = false; + var authTimeout; + if (this.authContextOptions.timeout) { + authTimeout = setTimeout(function () { + this$1.timedOut = true; + }, this.authContextOptions.timeout); + } + return new Promise$1(function (resolve, reject) { var redirectUriParser = document.createElement('a'); redirectUriParser.href = redirectUri; @@ -910,7 +915,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { reject(new Error('OAuth redirect has occurred but no query or hash parameters were found.')); } - + clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; if (this$1.authContextOptions.iframe) { @@ -922,9 +927,19 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } else { this$1.authWindow.close(); } + } else { + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); + } + } + } catch (e) { // DOMException: Blocked a frame with origin from accessing a cross-origin frame. + if (this$1.timedOut) { + clearInterval(poolingInterval); + poolingInterval = null; + reject(new Error('auth_timeout')); } - } catch (e) { - // Ignore DOMException: Blocked a frame with origin from accessing a cross-origin frame. } }, 250); }) diff --git a/package.json b/package.json index f46b1f6..8ef0c4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bsarvari/vue-authenticate", - "version": "1.3.5-beta.1.1", + "version": "1.3.5-beta.1.2", "description": "Authentication library for Vue.js", "main": "dist/vue-authenticate.js", "module": "dist/vue-authenticate.es2015.js", From bc3549c7836f051e9495204a8f83f479d954cf83 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Mon, 16 Apr 2018 13:29:26 +0200 Subject: [PATCH 12/16] fix stale iframe.contentWindow reference problem in Edge --- src/oauth/oauth-context.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/oauth/oauth-context.js b/src/oauth/oauth-context.js index 0c11054..0f77dbc 100644 --- a/src/oauth/oauth-context.js +++ b/src/oauth/oauth-context.js @@ -22,7 +22,6 @@ export default class OAuthContext { this.iframe = document.createElement('iframe') this.iframe.src = this.url this.iframeTarget.appendChild(this.iframe) - this.authWindow = this.iframe.contentWindow } else { this.authWindow = window.open(this.url, this.name, this._stringifyOptions()) if (this.authWindow && this.authWindow.focus) { @@ -69,7 +68,7 @@ export default class OAuthContext { const redirectUriPath = getFullUrlPath(redirectUriParser) let poolingInterval = setInterval(() => { - if (!this.authContextOptions.iframe) { + if (!this.iframe) { if (!this.authWindow || this.authWindow.closed || this.authWindow.closed === undefined) { clearInterval(poolingInterval) poolingInterval = null @@ -78,12 +77,13 @@ export default class OAuthContext { } try { - const authWindowPath = getFullUrlPath(this.authWindow.location) + const authWindow = this.authWindow || this.iframe.contentWindow + const authWindowPath = getFullUrlPath(authWindow.location) if (authWindowPath === redirectUriPath) { - if (this.authWindow.location.search || this.authWindow.location.hash) { - const query = parseQueryString(this.authWindow.location.search.substring(1).replace(/\/$/, '')) - const hash = parseQueryString(this.authWindow.location.hash.substring(1).replace(/[\/$]/, '')) + if (authWindow.location.search || authWindow.location.hash) { + const query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')) + const hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')) let params = objectExtend({}, query) params = objectExtend(params, hash) @@ -98,7 +98,7 @@ export default class OAuthContext { clearTimeout(authTimeout) clearInterval(poolingInterval) poolingInterval = null - if (this.authContextOptions.iframe) { + if (this.iframe) { if (this.authContextOptions.iframeTarget) { this.iframeTarget.removeChild(this.iframe) } else { From 9ec97aa54c35f9af24fa1ffeedacf3b6a6ec4174 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Mon, 16 Apr 2018 14:09:23 +0200 Subject: [PATCH 13/16] Increase module version + dist resources --- dist/vue-authenticate.common.js | 16 ++++++++-------- dist/vue-authenticate.es2015.js | 16 ++++++++-------- dist/vue-authenticate.js | 16 ++++++++-------- dist/vue-authenticate.min.js | 4 ++-- example/vue-authenticate.js | 16 ++++++++-------- package.json | 2 +- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index 7d2752f..9afd003 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.2 + * vue-authenticate v1.3.5-beta.1.3 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -836,7 +836,6 @@ OAuthContext.prototype.open = function open (redirectUri, skipPooling) { this.iframe = document.createElement('iframe'); this.iframe.src = this.url; this.iframeTarget.appendChild(this.iframe); - this.authWindow = this.iframe.contentWindow; } else { this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); if (this.authWindow && this.authWindow.focus) { @@ -885,7 +884,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.authContextOptions.iframe) { + if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { clearInterval(poolingInterval); poolingInterval = null; @@ -894,12 +893,13 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } try { - var authWindowPath = getFullUrlPath(this$1.authWindow.location); + var authWindow = this$1.authWindow || this$1.iframe.contentWindow; + var authWindowPath = getFullUrlPath(authWindow.location); if (authWindowPath === redirectUriPath) { - if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { - var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindow.location.search || authWindow.location.hash) { + var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -914,7 +914,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; - if (this$1.authContextOptions.iframe) { + if (this$1.iframe) { if (this$1.authContextOptions.iframeTarget) { this$1.iframeTarget.removeChild(this$1.iframe); } else { diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index 4c0d47a..12967bf 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.2 + * vue-authenticate v1.3.5-beta.1.3 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -834,7 +834,6 @@ OAuthContext.prototype.open = function open (redirectUri, skipPooling) { this.iframe = document.createElement('iframe'); this.iframe.src = this.url; this.iframeTarget.appendChild(this.iframe); - this.authWindow = this.iframe.contentWindow; } else { this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); if (this.authWindow && this.authWindow.focus) { @@ -883,7 +882,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.authContextOptions.iframe) { + if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { clearInterval(poolingInterval); poolingInterval = null; @@ -892,12 +891,13 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } try { - var authWindowPath = getFullUrlPath(this$1.authWindow.location); + var authWindow = this$1.authWindow || this$1.iframe.contentWindow; + var authWindowPath = getFullUrlPath(authWindow.location); if (authWindowPath === redirectUriPath) { - if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { - var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindow.location.search || authWindow.location.hash) { + var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -912,7 +912,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; - if (this$1.authContextOptions.iframe) { + if (this$1.iframe) { if (this$1.authContextOptions.iframeTarget) { this$1.iframeTarget.removeChild(this$1.iframe); } else { diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index 462e334..c88fe6b 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.2 + * vue-authenticate v1.3.5-beta.1.3 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -840,7 +840,6 @@ OAuthContext.prototype.open = function open (redirectUri, skipPooling) { this.iframe = document.createElement('iframe'); this.iframe.src = this.url; this.iframeTarget.appendChild(this.iframe); - this.authWindow = this.iframe.contentWindow; } else { this.authWindow = window.open(this.url, this.name, this._stringifyOptions()); if (this.authWindow && this.authWindow.focus) { @@ -889,7 +888,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var redirectUriPath = getFullUrlPath(redirectUriParser); var poolingInterval = setInterval(function () { - if (!this$1.authContextOptions.iframe) { + if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { clearInterval(poolingInterval); poolingInterval = null; @@ -898,12 +897,13 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } try { - var authWindowPath = getFullUrlPath(this$1.authWindow.location); + var authWindow = this$1.authWindow || this$1.iframe.contentWindow; + var authWindowPath = getFullUrlPath(authWindow.location); if (authWindowPath === redirectUriPath) { - if (this$1.authWindow.location.search || this$1.authWindow.location.hash) { - var query = parseQueryString(this$1.authWindow.location.search.substring(1).replace(/\/$/, '')); - var hash = parseQueryString(this$1.authWindow.location.hash.substring(1).replace(/[\/$]/, '')); + if (authWindow.location.search || authWindow.location.hash) { + var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); + var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); var params = objectExtend({}, query); params = objectExtend(params, hash); @@ -918,7 +918,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { clearTimeout(authTimeout); clearInterval(poolingInterval); poolingInterval = null; - if (this$1.authContextOptions.iframe) { + if (this$1.iframe) { if (this$1.authContextOptions.iframeTarget) { this$1.iframeTarget.removeChild(this$1.iframe); } else { diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index c69a26e..5446cd3 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -1,7 +1,7 @@ /*! - * vue-authenticate v1.3.5-beta.1.2 + * vue-authenticate v1.3.5-beta.1.3 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueAuthenticate=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return null!==t&&"object"==typeof t}function n(t){return"string"==typeof t}function r(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function a(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function s(t){return t.protocol+"//"+t.host+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function u(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function h(t){var e;if("undefined"!=typeof module&&module.exports)try{e=require("buffer").Buffer}catch(t){}var o=String.fromCharCode,n=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g"),r=function(t){switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),n=e-65536;return o(55296+(n>>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function c(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e Date: Thu, 10 May 2018 15:18:37 +0200 Subject: [PATCH 14/16] When IE11 converted the redirect URL string using a URI parser it added a port even though the original URL didn't contain any. Since this port isn't present in the auth iframe's location, the authentication always timed out. This commit fixes this issue by omitting the parser technique. --- dist/vue-authenticate.common.js | 8 ++------ dist/vue-authenticate.es2015.js | 8 ++------ dist/vue-authenticate.js | 8 ++------ dist/vue-authenticate.min.js | 4 ++-- example/vue-authenticate.js | 8 ++------ package.json | 2 +- src/oauth/oauth-context.js | 6 +----- 7 files changed, 12 insertions(+), 32 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index 9afd003..269385f 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.3 + * vue-authenticate v1.3.5-beta.1.4 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -879,10 +879,6 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } return new Promise$1(function (resolve, reject) { - var redirectUriParser = document.createElement('a'); - redirectUriParser.href = redirectUri; - var redirectUriPath = getFullUrlPath(redirectUriParser); - var poolingInterval = setInterval(function () { if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { @@ -896,7 +892,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var authWindow = this$1.authWindow || this$1.iframe.contentWindow; var authWindowPath = getFullUrlPath(authWindow.location); - if (authWindowPath === redirectUriPath) { + if (authWindowPath === redirectUri) { if (authWindow.location.search || authWindow.location.hash) { var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index 12967bf..8b1b3f4 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.3 + * vue-authenticate v1.3.5-beta.1.4 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -877,10 +877,6 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } return new Promise$1(function (resolve, reject) { - var redirectUriParser = document.createElement('a'); - redirectUriParser.href = redirectUri; - var redirectUriPath = getFullUrlPath(redirectUriParser); - var poolingInterval = setInterval(function () { if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { @@ -894,7 +890,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var authWindow = this$1.authWindow || this$1.iframe.contentWindow; var authWindowPath = getFullUrlPath(authWindow.location); - if (authWindowPath === redirectUriPath) { + if (authWindowPath === redirectUri) { if (authWindow.location.search || authWindow.location.hash) { var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index c88fe6b..c910088 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -1,5 +1,5 @@ /*! - * vue-authenticate v1.3.5-beta.1.3 + * vue-authenticate v1.3.5-beta.1.4 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ @@ -883,10 +883,6 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } return new Promise$1(function (resolve, reject) { - var redirectUriParser = document.createElement('a'); - redirectUriParser.href = redirectUri; - var redirectUriPath = getFullUrlPath(redirectUriParser); - var poolingInterval = setInterval(function () { if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { @@ -900,7 +896,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var authWindow = this$1.authWindow || this$1.iframe.contentWindow; var authWindowPath = getFullUrlPath(authWindow.location); - if (authWindowPath === redirectUriPath) { + if (authWindowPath === redirectUri) { if (authWindow.location.search || authWindow.location.hash) { var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index 5446cd3..1091049 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -1,7 +1,7 @@ /*! - * vue-authenticate v1.3.5-beta.1.3 + * vue-authenticate v1.3.5-beta.1.4 * https://github.com/dgrubelic/vue-authenticate * Released under the MIT License. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueAuthenticate=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return null!==t&&"object"==typeof t}function n(t){return"string"==typeof t}function r(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function a(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function s(t){return t.protocol+"//"+t.host+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function u(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function c(t){var e;if("undefined"!=typeof module&&module.exports)try{e=require("buffer").Buffer}catch(t){}var o=String.fromCharCode,n=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g"),r=function(t){switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),n=e-65536;return o(55296+(n>>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e { - const redirectUriParser = document.createElement('a') - redirectUriParser.href = redirectUri - const redirectUriPath = getFullUrlPath(redirectUriParser) - let poolingInterval = setInterval(() => { if (!this.iframe) { if (!this.authWindow || this.authWindow.closed || this.authWindow.closed === undefined) { @@ -80,7 +76,7 @@ export default class OAuthContext { const authWindow = this.authWindow || this.iframe.contentWindow const authWindowPath = getFullUrlPath(authWindow.location) - if (authWindowPath === redirectUriPath) { + if (authWindowPath === redirectUri) { if (authWindow.location.search || authWindow.location.hash) { const query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')) const hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')) From 55c242149daefc9bc81444e2e088a6815e0a4567 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Fri, 11 May 2018 12:50:17 +0200 Subject: [PATCH 15/16] ASH-54: a less error-prone way to getFullUrlPath (to please both IE11 and older Safari) --- src/oauth/oauth-context.js | 6 +++++- src/utils.js | 10 ++++++++-- src/utils.test.js | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/utils.test.js diff --git a/src/oauth/oauth-context.js b/src/oauth/oauth-context.js index b92b08f..fd8cb84 100644 --- a/src/oauth/oauth-context.js +++ b/src/oauth/oauth-context.js @@ -63,6 +63,10 @@ export default class OAuthContext { } return new Promise((resolve, reject) => { + const redirectUriParser = document.createElement('a') + redirectUriParser.href = redirectUri + const redirectUriPath = getFullUrlPath(redirectUriParser) + let poolingInterval = setInterval(() => { if (!this.iframe) { if (!this.authWindow || this.authWindow.closed || this.authWindow.closed === undefined) { @@ -76,7 +80,7 @@ export default class OAuthContext { const authWindow = this.authWindow || this.iframe.contentWindow const authWindowPath = getFullUrlPath(authWindow.location) - if (authWindowPath === redirectUri) { + if (authWindowPath === redirectUriPath) { if (authWindow.location.search || authWindow.location.hash) { const query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')) const hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')) diff --git a/src/utils.js b/src/utils.js index 039d493..e2c64a5 100644 --- a/src/utils.js +++ b/src/utils.js @@ -109,8 +109,14 @@ export function joinUrl(baseUrl, url) { * @param {Location} location * @return {String} */ -export function getFullUrlPath(location) { - return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) +export function getFullUrlPath (location) { + const isHttps = location.protocol === 'https:' + let port = location.port + if (!port || port === '0') { + port = isHttps ? '443' : '80' + } + return location.protocol + '//' + location.hostname + ':' + port + + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** diff --git a/src/utils.test.js b/src/utils.test.js new file mode 100644 index 0000000..41a5d96 --- /dev/null +++ b/src/utils.test.js @@ -0,0 +1,34 @@ +import { getFullUrlPath } from './utils' + +const urlParser = url => { + const a = document.createElement('a') + a.href = url + return a +} + +describe('vue-authenticate/utils', () => { + describe('#getFullUrlPath', () => { + describe('with no port in URL', () => { + it('should add port 80 for http URL', () => { + expect(getFullUrlPath(urlParser('http://example.com/auth'))).toBe('http://example.com:80/auth') + }) + it('should add port 443 for https URL', () => { + expect(getFullUrlPath(urlParser('https://example.com/auth'))).toBe('https://example.com:443/auth') + }) + }) + describe('with port in URL', () => { + it('should respect port other than "0" for http URL', () => { + expect(getFullUrlPath(urlParser('http://example.com:2999/auth'))).toBe('http://example.com:2999/auth') + }) + it('should respect port other than "0" for https URL', () => { + expect(getFullUrlPath(urlParser('https://example.com:1/auth'))).toBe('https://example.com:1/auth') + }) + it('should convert port "0" to "80" for http URL', () => { + expect(getFullUrlPath(urlParser('http://example.com:0/auth'))).toBe('http://example.com:80/auth') + }) + it('should convert port "0" to "443" for https URL', () => { + expect(getFullUrlPath(urlParser('https://example.com:0/auth'))).toBe('https://example.com:443/auth') + }) + }) + }) +}) From c1270f4b22a52b40733751467f56de34b7b66a93 Mon Sep 17 00:00:00 2001 From: Balazs Sarvari Date: Fri, 11 May 2018 14:28:56 +0200 Subject: [PATCH 16/16] ASH-54: generated artifacts --- dist/vue-authenticate.common.js | 16 +++++++++++++--- dist/vue-authenticate.es2015.js | 16 +++++++++++++--- dist/vue-authenticate.js | 16 +++++++++++++--- dist/vue-authenticate.min.js | 2 +- example/vue-authenticate.js | 16 +++++++++++++--- 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/dist/vue-authenticate.common.js b/dist/vue-authenticate.common.js index 269385f..a6edad1 100644 --- a/dist/vue-authenticate.common.js +++ b/dist/vue-authenticate.common.js @@ -115,8 +115,14 @@ function joinUrl(baseUrl, url) { * @param {Location} location * @return {String} */ -function getFullUrlPath(location) { - return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) +function getFullUrlPath (location) { + var isHttps = location.protocol === 'https:'; + var port = location.port; + if (!port || port === '0') { + port = isHttps ? '443' : '80'; + } + return location.protocol + '//' + location.hostname + ':' + port + + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** @@ -879,6 +885,10 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } return new Promise$1(function (resolve, reject) { + var redirectUriParser = document.createElement('a'); + redirectUriParser.href = redirectUri; + var redirectUriPath = getFullUrlPath(redirectUriParser); + var poolingInterval = setInterval(function () { if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { @@ -892,7 +902,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var authWindow = this$1.authWindow || this$1.iframe.contentWindow; var authWindowPath = getFullUrlPath(authWindow.location); - if (authWindowPath === redirectUri) { + if (authWindowPath === redirectUriPath) { if (authWindow.location.search || authWindow.location.hash) { var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); diff --git a/dist/vue-authenticate.es2015.js b/dist/vue-authenticate.es2015.js index 8b1b3f4..6c1fc8d 100644 --- a/dist/vue-authenticate.es2015.js +++ b/dist/vue-authenticate.es2015.js @@ -113,8 +113,14 @@ function joinUrl(baseUrl, url) { * @param {Location} location * @return {String} */ -function getFullUrlPath(location) { - return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) +function getFullUrlPath (location) { + var isHttps = location.protocol === 'https:'; + var port = location.port; + if (!port || port === '0') { + port = isHttps ? '443' : '80'; + } + return location.protocol + '//' + location.hostname + ':' + port + + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** @@ -877,6 +883,10 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } return new Promise$1(function (resolve, reject) { + var redirectUriParser = document.createElement('a'); + redirectUriParser.href = redirectUri; + var redirectUriPath = getFullUrlPath(redirectUriParser); + var poolingInterval = setInterval(function () { if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { @@ -890,7 +900,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var authWindow = this$1.authWindow || this$1.iframe.contentWindow; var authWindowPath = getFullUrlPath(authWindow.location); - if (authWindowPath === redirectUri) { + if (authWindowPath === redirectUriPath) { if (authWindow.location.search || authWindow.location.hash) { var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); diff --git a/dist/vue-authenticate.js b/dist/vue-authenticate.js index c910088..fcb482f 100644 --- a/dist/vue-authenticate.js +++ b/dist/vue-authenticate.js @@ -119,8 +119,14 @@ function joinUrl(baseUrl, url) { * @param {Location} location * @return {String} */ -function getFullUrlPath(location) { - return location.protocol + '//' + location.host + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) +function getFullUrlPath (location) { + var isHttps = location.protocol === 'https:'; + var port = location.port; + if (!port || port === '0') { + port = isHttps ? '443' : '80'; + } + return location.protocol + '//' + location.hostname + ':' + port + + (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname) } /** @@ -883,6 +889,10 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { } return new Promise$1(function (resolve, reject) { + var redirectUriParser = document.createElement('a'); + redirectUriParser.href = redirectUri; + var redirectUriPath = getFullUrlPath(redirectUriParser); + var poolingInterval = setInterval(function () { if (!this$1.iframe) { if (!this$1.authWindow || this$1.authWindow.closed || this$1.authWindow.closed === undefined) { @@ -896,7 +906,7 @@ OAuthContext.prototype.pooling = function pooling (redirectUri) { var authWindow = this$1.authWindow || this$1.iframe.contentWindow; var authWindowPath = getFullUrlPath(authWindow.location); - if (authWindowPath === redirectUri) { + if (authWindowPath === redirectUriPath) { if (authWindow.location.search || authWindow.location.hash) { var query = parseQueryString(authWindow.location.search.substring(1).replace(/\/$/, '')); var hash = parseQueryString(authWindow.location.hash.substring(1).replace(/[\/$]/, '')); diff --git a/dist/vue-authenticate.min.js b/dist/vue-authenticate.min.js index 1091049..7aad764 100644 --- a/dist/vue-authenticate.min.js +++ b/dist/vue-authenticate.min.js @@ -4,4 +4,4 @@ * Released under the MIT License. */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.VueAuthenticate=e()}(this,function(){"use strict";function t(t){return t.replace(/([\:\-\_]+(.))/g,function(t,e,o,n){return n?o.toUpperCase():o})}function e(t){return void 0===t}function o(t){return null!==t&&"object"==typeof t}function n(t){return"string"==typeof t}function r(t){return"function"==typeof t}function i(t,e){return null==t||null==e?t:(Object.keys(e).forEach(function(o){"[object Object]"==Object.prototype.toString.call(e[o])?"[object Object]"!=Object.prototype.toString.call(t[o])?t[o]=e[o]:t[o]=i(t[o],e[o]):t[o]=e[o]}),t)}function a(t,e){if(/^(?:[a-z]+:)?\/\//i.test(e))return e;var o=[t,e].join("/");return function(t){return t.replace(/[\/]+/g,"/").replace(/\/\?/g,"?").replace(/\/\#/g,"#").replace(/\:\//g,"://")}(o)}function s(t){return t.protocol+"//"+t.host+(/^\//.test(t.pathname)?t.pathname:"/"+t.pathname)}function u(t){var e,o,n={};return(t||"").split("&").forEach(function(t){t&&(o=t.split("="),e=decodeURIComponent(o[0]),n[e]=!o[1]||decodeURIComponent(o[1]))}),n}function c(t){var e;if("undefined"!=typeof module&&module.exports)try{e=require("buffer").Buffer}catch(t){}var o=String.fromCharCode,n=new RegExp(["[À-ß][€-¿]","[à-ï][€-¿]{2}","[ð-÷][€-¿]{3}"].join("|"),"g"),r=function(t){switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),n=e-65536;return o(55296+(n>>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e>>10))+o(56320+(1023&n));case 3:return o((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return o((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},i=function(t){return t.replace(n,r)};return(e?function(t){return(t.constructor===e.constructor?t:new e(t,"base64")).toString()}:function(t){return i(atob(t))})(String(t).replace(/[-_]/g,function(t){return"-"===t?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))}function h(t){if(0===t.length)return{};var e={},o=new RegExp("\\s*;\\s*");return t.split(o).forEach(function(t){var o=t.split("="),n=o[0],r=o[1],i=decodeURIComponent(n),a=decodeURIComponent(r);e[i]=a}),e}function p(t){var e=t.path,o=t.domain,n=t.expires,r=t.secure;return[void 0===e||null===e?"":";path="+e,void 0===o||null===o?"":";domain="+o,void 0===n||null===n?"":";expires="+n.toUTCString(),void 0===r||null===r||!1===r?"":";secure"].join("")}function l(t,e,o){return[encodeURIComponent(t),"=",encodeURIComponent(e),p(o)].join("")}function d(){}function f(t,e){return function(){t.apply(e,arguments)}}function m(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],_(t,this)}function g(t,e){for(;3===t._state;)t=t._value;if(0===t._state)return void t._deferreds.push(e);t._handled=!0,m._immediateFn(function(){var o=1===t._state?e.onFulfilled:e.onRejected;if(null===o)return void(1===t._state?v:y)(e.promise,t._value);var n;try{n=o(t._value)}catch(t){return void y(e.promise,t)}v(e.promise,n)})}function v(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var o=e.then;if(e instanceof m)return t._state=3,t._value=e,void w(t);if("function"==typeof o)return void _(f(o,e),t)}t._state=1,t._value=e,w(t)}catch(e){y(t,e)}}function y(t,e){t._state=2,t._value=e,w(t)}function w(t){2===t._state&&0===t._deferreds.length&&m._immediateFn(function(){t._handled||m._unhandledRejectionFn(t._value)});for(var e=0,o=t._deferreds.length;e