oauth2-popup-flow
Advanced tools
Comparing version 0.1.0 to 0.1.1
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.OAuth2PopupFlow=t():e.OAuth2PopupFlow=t()}(window,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";var r=this&&this.__assign||Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},o=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}u((r=r.apply(e,t||[])).next())})},i=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=r[2&i[0]?"return":i[0]?"throw":"next"])&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[0,o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,s])}}};Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e){this.authorizationUri=e.authorizationUri,this.clientId=e.clientId,this.redirectUri=e.redirectUri,this.scope=e.scope,this.responseType=e.responseType||"token",this.accessTokenStorageKey=e.accessTokenStorageKey||"token",this.accessTokenResponseKey=e.accessTokenResponseKey||"access_token",this.storage=e.storage||window.localStorage,this.pollingTime=e.pollingTime||200,this.additionalAuthorizationParameters=e.additionalAuthorizationParameters,this.tokenValidator=e.tokenValidator,this.beforePopup=e.beforePopup,this.afterResponse=e.afterResponse}return Object.defineProperty(e.prototype,"_rawToken",{get:function(){return this.storage.getItem(this.accessTokenStorageKey)||void 0},set:function(e){null!==e&&void 0!==e&&this.storage.setItem(this.accessTokenStorageKey,e)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"_rawTokenPayload",{get:function(){var t=this._rawToken;if(t){var n=t.split(".")[1];if(n){var r=window.atob(n);return e.jsonParseOrUndefined(r)}}},enumerable:!0,configurable:!0}),e.prototype.loggedIn=function(){var e=this._rawTokenPayload;if(!e)return!1;if(this.tokenValidator){var t=this._rawToken;if(!this.tokenValidator({payload:e,token:t}))return!1}var n=e.exp;return!!n&&!((new Date).getTime()>1e3*n)},e.prototype.tokenExpired=function(){var e=this._rawTokenPayload;if(!e)return!1;var t=e.exp;return!!t&&!((new Date).getTime()<=1e3*t)},e.prototype.logout=function(){this.storage.removeItem(this.accessTokenStorageKey)},e.prototype.handleRedirect=function(){if(!window.location.href.startsWith(this.redirectUri))return"REDIRECT_URI_MISMATCH";var t=window.location.hash;if(!t)return"FALSY_HASH";var n=/#(.*)/.exec(t);if(!n)return"NO_HASH_MATCH";var r=n[1],o=e.decodeUriToObject(r);this.afterResponse&&this.afterResponse(o);var i=o[this.accessTokenResponseKey];return i?(this._rawToken=i,window.location.hash="","SUCCESS"):"FALSY_TOKEN"},e.prototype.tryLoginPopup=function(){return o(this,void 0,void 0,function(){var t,n;return i(this,function(o){switch(o.label){case 0:return this.loggedIn()?[2,"ALREADY_LOGGED_IN"]:this.beforePopup?[4,Promise.resolve(this.beforePopup())]:[3,2];case 1:o.sent(),o.label=2;case 2:return t="function"==typeof this.additionalAuthorizationParameters?this.additionalAuthorizationParameters():"object"==typeof this.additionalAuthorizationParameters?this.additionalAuthorizationParameters:{},(n=window.open(this.authorizationUri+"?"+e.encodeObjectToUri(r({client_id:this.clientId,response_type:this.responseType,redirect_uri:this.redirectUri,scope:this.scope},t))))?[4,this.authenticated()]:[2,"POPUP_FAILED"];case 3:return o.sent(),n.close(),[2,"SUCCESS"]}})})},e.prototype.authenticated=function(){return o(this,void 0,void 0,function(){return i(this,function(t){switch(t.label){case 0:return this.loggedIn()?[3,2]:[4,e.time(this.pollingTime)];case 1:return t.sent(),[3,0];case 2:return[2]}})})},e.prototype.token=function(){return o(this,void 0,void 0,function(){var e;return i(this,function(t){switch(t.label){case 0:return[4,this.authenticated()];case 1:if(t.sent(),!(e=this._rawToken))throw new Error("Token was falsy after being authenticated.");return[2,e]}})})},e.prototype.tokenPayload=function(){return o(this,void 0,void 0,function(){var e;return i(this,function(t){switch(t.label){case 0:return[4,this.authenticated()];case 1:if(t.sent(),!(e=this._rawTokenPayload))throw new Error("Token payload was falsy after being authenticated.");return[2,e]}})})},e.jsonParseOrUndefined=function(e){try{return JSON.parse(e)}catch(e){return}},e.time=function(e){return new Promise(function(t){return window.setTimeout(function(){return t("TIMER")},e)})},e.decodeUri=function(e){try{return decodeURIComponent(e)}catch(t){return e}},e.encodeObjectToUri=function(e){return Object.keys(e).map(function(t){return{key:t,value:e[t]}}).map(function(e){var t=e.key,n=e.value;return encodeURIComponent(t)+"="+encodeURIComponent(n)}).join("&")},e.decodeUriToObject=function(e){var t=this;return e.split("&").reduce(function(e,n){var r=n.split("="),o=r[0],i=r[1],a=t.decodeUri(o),s=t.decodeUri(i);return e[a]=s,e},{})},e}();t.OAuth2PopupFlow=a}])}); | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.OAuth2PopupFlow=t():e.OAuth2PopupFlow=t()}(window,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";var r=this&&this.__assign||function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e}).apply(this,arguments)},o=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}u((r=r.apply(e,t||[])).next())})},i=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,s])}}};Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e){this.authorizationUri=e.authorizationUri,this.clientId=e.clientId,this.redirectUri=e.redirectUri,this.scope=e.scope,this.responseType=e.responseType||"token",this.accessTokenStorageKey=e.accessTokenStorageKey||"token",this.accessTokenResponseKey=e.accessTokenResponseKey||"access_token",this.storage=e.storage||window.localStorage,this.pollingTime=e.pollingTime||200,this.additionalAuthorizationParameters=e.additionalAuthorizationParameters,this.tokenValidator=e.tokenValidator,this.beforePopup=e.beforePopup,this.afterResponse=e.afterResponse,this._eventListeners={}}return Object.defineProperty(e.prototype,"_rawToken",{get:function(){return this.storage.getItem(this.accessTokenStorageKey)||void 0},set:function(e){null!=e&&this.storage.setItem(this.accessTokenStorageKey,e)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"_rawTokenPayload",{get:function(){var t=this._rawToken;if(t){var n=t.split(".")[1];if(n){var r=window.atob(n);return e.jsonParseOrUndefined(r)}}},enumerable:!0,configurable:!0}),e.prototype.loggedIn=function(){var e=this._rawTokenPayload;if(!e)return!1;if(this.tokenValidator){var t=this._rawToken;if(!this.tokenValidator({payload:e,token:t}))return!1}var n=e.exp;return!!n&&!((new Date).getTime()>1e3*n)},e.prototype.tokenExpired=function(){var e=this._rawTokenPayload;if(!e)return!1;var t=e.exp;return!!t&&!((new Date).getTime()<=1e3*t)},e.prototype.logout=function(){this.storage.removeItem(this.accessTokenStorageKey),this.dispatchEvent(new Event("logout"))},e.prototype.handleRedirect=function(){if(!window.location.href.startsWith(this.redirectUri))return"REDIRECT_URI_MISMATCH";var t=window.location.hash;if(!t)return"FALSY_HASH";var n=/#(.*)/.exec(t);if(!n)return"NO_HASH_MATCH";var r=n[1],o=e.decodeUriToObject(r);this.afterResponse&&this.afterResponse(o);var i=o[this.accessTokenResponseKey];return i?(this._rawToken=i,window.location.hash="","SUCCESS"):"FALSY_TOKEN"},e.prototype.addEventListener=function(e,t){var n=this._eventListeners[e]||[];n.push(t),this._eventListeners[e]=n},e.prototype.dispatchEvent=function(e){for(var t=0,n=this._eventListeners[e.type]||[];t<n.length;t++){var r=n[t];("function"==typeof r?r:"object"==typeof r&&"function"==typeof r.handleEvent?r.handleEvent.bind(r):function(){})(e)}return!0},e.prototype.removeEventListener=function(e,t){var n=this._eventListeners[e]||[];this._eventListeners[e]=n.filter(function(e){return e!==t})},e.prototype.tryLoginPopup=function(){return o(this,void 0,void 0,function(){var t,n;return i(this,function(o){switch(o.label){case 0:return this.loggedIn()?[2,"ALREADY_LOGGED_IN"]:this.beforePopup?[4,Promise.resolve(this.beforePopup())]:[3,2];case 1:o.sent(),o.label=2;case 2:return t="function"==typeof this.additionalAuthorizationParameters?this.additionalAuthorizationParameters():"object"==typeof this.additionalAuthorizationParameters?this.additionalAuthorizationParameters:{},(n=window.open(this.authorizationUri+"?"+e.encodeObjectToUri(r({client_id:this.clientId,response_type:this.responseType,redirect_uri:this.redirectUri,scope:this.scope},t))))?[4,this.authenticated()]:[2,"POPUP_FAILED"];case 3:return o.sent(),n.close(),this.dispatchEvent(new Event("login")),[2,"SUCCESS"]}})})},e.prototype.authenticated=function(){return o(this,void 0,void 0,function(){return i(this,function(t){switch(t.label){case 0:return this.loggedIn()?[3,2]:[4,e.time(this.pollingTime)];case 1:return t.sent(),[3,0];case 2:return[2]}})})},e.prototype.token=function(){return o(this,void 0,void 0,function(){var e;return i(this,function(t){switch(t.label){case 0:return[4,this.authenticated()];case 1:if(t.sent(),!(e=this._rawToken))throw new Error("Token was falsy after being authenticated.");return[2,e]}})})},e.prototype.tokenPayload=function(){return o(this,void 0,void 0,function(){var e;return i(this,function(t){switch(t.label){case 0:return[4,this.authenticated()];case 1:if(t.sent(),!(e=this._rawTokenPayload))throw new Error("Token payload was falsy after being authenticated.");return[2,e]}})})},e.jsonParseOrUndefined=function(e){try{return JSON.parse(e)}catch(e){return}},e.time=function(e){return new Promise(function(t){return window.setTimeout(function(){return t("TIMER")},e)})},e.decodeUri=function(e){try{return decodeURIComponent(e)}catch(t){return e}},e.encodeObjectToUri=function(e){return Object.keys(e).map(function(t){return{key:t,value:e[t]}}).map(function(e){var t=e.key,n=e.value;return encodeURIComponent(t)+"="+encodeURIComponent(n)}).join("&")},e.decodeUriToObject=function(e){var t=this;return e.split("&").reduce(function(e,n){var r=n.split("="),o=r[0],i=r[1],a=t.decodeUri(o),s=t.decodeUri(i);return e[a]=s,e},{})},e}();t.OAuth2PopupFlow=a}])}); | ||
//# sourceMappingURL=oauth2-popup-flow.js.map |
{ | ||
"name": "oauth2-popup-flow", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "A very simple oauth2 implicit flow library that uses window.open.", | ||
@@ -8,3 +8,3 @@ "main": "./dist/oauth2-popup-flow.js", | ||
"scripts": { | ||
"test": "nyc jasmine-ts \"src/**/*.spec.ts\" | coveralls", | ||
"test": "jest --coverage --coverageReporters=text-lcov | coveralls", | ||
"build": "webpack -p", | ||
@@ -20,28 +20,14 @@ "docs": "typedoc --out docs/ src/" | ||
"devDependencies": { | ||
"@types/jasmine": "^2.8.6", | ||
"@types/jest": "^23.3.12", | ||
"awesome-typescript-loader": "^3.5.0", | ||
"coveralls": "^3.0.0", | ||
"jasmine-spec-reporter": "^4.2.1", | ||
"jasmine-ts": "^0.2.1", | ||
"jsdom": "^11.6.2", | ||
"nyc": "^11.4.1", | ||
"jest": "^23.6.0", | ||
"jsdom": "^13.1.0", | ||
"ts-jest": "^23.10.5", | ||
"typedoc": "^0.10.0", | ||
"typescript": "^2.7.2", | ||
"webpack": "^4.0.1", | ||
"webpack-cli": "^2.0.9" | ||
"typescript": "^3.2.2", | ||
"webpack": "^4.28.4", | ||
"webpack-cli": "^3.2.1" | ||
}, | ||
"nyc": { | ||
"include": [ | ||
"src/**/*.ts" | ||
], | ||
"exclude": [ | ||
"src/**/*.spec.ts" | ||
], | ||
"reporter": [ | ||
"text-lcov" | ||
], | ||
"extension": [ | ||
".ts" | ||
] | ||
} | ||
"dependencies": {} | ||
} |
@@ -30,3 +30,3 @@ ``` | ||
* Simplicity as a feature—only 187 SLOC. | ||
* Simplicity as a feature—only 189 SLOC. | ||
* No dependencies. | ||
@@ -33,0 +33,0 @@ * Easily integrates with React, Angular, Vue etc. |
import { OAuth2PopupFlow } from './'; | ||
export class DeferredPromise<T> implements Promise<T> { | ||
private _promise: Promise<T>; | ||
resolve!: (t?: T) => void; | ||
reject!: (error?: any) => void; | ||
then: <TResult1 = T, TResult2 = never>( | ||
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, | ||
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null, | ||
) => Promise<TResult1 | TResult2>; | ||
catch: <TResult = never>( | ||
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null, | ||
) => Promise<T | TResult>; | ||
state: 'pending' | 'fulfilled' | 'rejected'; | ||
constructor() { | ||
this.state = 'pending'; | ||
this._promise = new Promise((resolve, reject) => { | ||
this.resolve = (value?: T | PromiseLike<T> | undefined) => { | ||
this.state = 'fulfilled'; | ||
resolve(value); | ||
}; | ||
this.reject = (reason: any) => { | ||
this.state = 'rejected'; | ||
reject(reason); | ||
}; | ||
}); | ||
this.then = this._promise.then.bind(this._promise) as any; | ||
this.catch = this._promise.catch.bind(this._promise) as any; | ||
} | ||
[Symbol.toStringTag] = 'Promise' as 'Promise'; | ||
} | ||
interface ExampleTokenPayload { | ||
exp: number, | ||
foo: string, | ||
bar: number, | ||
exp: number; | ||
foo: string; | ||
bar: number; | ||
} | ||
@@ -20,4 +52,8 @@ | ||
length: Object.keys(_storage).length, | ||
removeItem: (key: string) => { delete _storage[key]; }, | ||
setItem: (key: string, value: string) => { _storage[key] = value; }, | ||
removeItem: (key: string) => { | ||
delete _storage[key]; | ||
}, | ||
setItem: (key: string, value: string) => { | ||
_storage[key] = value; | ||
}, | ||
_storage, | ||
@@ -31,3 +67,3 @@ }; | ||
const validJson = '{"a": "some value", "b": 5}'; | ||
const parsed = OAuth2PopupFlow.jsonParseOrUndefined<{ a: string, b: number }>(validJson)!; | ||
const parsed = OAuth2PopupFlow.jsonParseOrUndefined<{ a: string; b: number }>(validJson)!; | ||
expect(parsed).toBeDefined(); | ||
@@ -50,12 +86,6 @@ expect(parsed.a).toBe('some value'); | ||
const race = await Promise.race([ | ||
OAuth2PopupFlow.time(10), | ||
fiveMilliseconds(), | ||
]); | ||
const race = await Promise.race([OAuth2PopupFlow.time(10), fiveMilliseconds()]); | ||
expect(race).toBe(5); | ||
const otherRace = await Promise.race([ | ||
OAuth2PopupFlow.time(0), | ||
fiveMilliseconds(), | ||
]); | ||
const otherRace = await Promise.race([OAuth2PopupFlow.time(0), fiveMilliseconds()]); | ||
expect(otherRace).toBe('TIMER'); | ||
@@ -95,8 +125,8 @@ }); | ||
it('creates instances from the `OAuth2PopupFlowOptions` object', () => { | ||
function beforePopup() { } | ||
function afterResponse() { } | ||
function beforePopup() {} | ||
function afterResponse() {} | ||
function tokenValidator() { | ||
return true; | ||
} | ||
const additionalAuthorizationParameters = { foo: 'bar', }; | ||
const additionalAuthorizationParameters = { foo: 'bar' }; | ||
@@ -233,3 +263,3 @@ const storage = createTestStorage(); | ||
}); | ||
it('doesn\'t allow `null` or `undefined` to be assigned to storage but allows strings', () => { | ||
it("doesn't allow `null` or `undefined` to be assigned to storage but allows strings", () => { | ||
const storage = createTestStorage(); | ||
@@ -282,3 +312,3 @@ | ||
}); | ||
it('returns `undefined` if it couldn\'t find the encoded payload in the token', () => { | ||
it("returns `undefined` if it couldn't find the encoded payload in the token", () => { | ||
const storage = createTestStorage(); | ||
@@ -297,3 +327,3 @@ | ||
}); | ||
it('returns `undefined` if it couldn\'t parse the JSON in the encoded payload', () => { | ||
it("returns `undefined` if it couldn't parse the JSON in the encoded payload", () => { | ||
const storage = createTestStorage(); | ||
@@ -312,3 +342,3 @@ | ||
'this is the payload section', | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -338,3 +368,3 @@ | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -372,3 +402,3 @@ | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -406,3 +436,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -440,3 +470,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -465,3 +495,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -490,3 +520,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -533,3 +563,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -558,3 +588,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -583,3 +613,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -611,3 +641,3 @@ storage._storage.token = exampleToken; | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -628,6 +658,36 @@ storage._storage.token = exampleToken; | ||
}); | ||
it('should dispatch a logout event', () => { | ||
const storage = createTestStorage(); | ||
const examplePayload = { | ||
foo: 'something', | ||
bar: 5, | ||
exp: Math.floor(new Date().getTime() / 1000) + 1000, | ||
}; | ||
const exampleToken = [ | ||
'blah blah header', | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section', | ||
].join('.'); | ||
storage._storage.token = exampleToken; | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>({ | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: 'http://localhost:8080/redirect', | ||
scope: 'openid profile', | ||
storage, | ||
}); | ||
const handler = jest.fn(); | ||
auth.addEventListener('logout', handler); | ||
expect(auth.loggedIn()).toBe(true); | ||
auth.logout(); | ||
expect(auth.loggedIn()).toBe(false); | ||
expect(handler).toBeCalledTimes(1); | ||
}); | ||
}); | ||
describe('handleRedirect', () => { | ||
it('returns early with `REDIRECT_URI_MISMATCH` if location doesn\'t match the redirect', () => { | ||
it("returns early with `REDIRECT_URI_MISMATCH` if location doesn't match the redirect", () => { | ||
const storage = createTestStorage(); | ||
@@ -668,19 +728,20 @@ | ||
}); | ||
it('returns early with `NO_HASH_MATCH` if hash doesn\'t match /#(.*)/', () => { | ||
const storage = createTestStorage(); | ||
// // this test won't pass because the js-dom environment will always add the `#` to the string | ||
// it('returns early with `NO_HASH_MATCH` if hash doesn\'t match /#(.*)/', () => { | ||
// const storage = createTestStorage(); | ||
const options = { | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: '', | ||
scope: 'openid profile', | ||
storage, | ||
}; | ||
// const options = { | ||
// authorizationUri: 'http://example.com/oauth/authorize', | ||
// clientId: 'some_test_client', | ||
// redirectUri: '', | ||
// scope: 'openid profile', | ||
// storage, | ||
// }; | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
window.location.hash = 'shouldn\t match'; | ||
// const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
// window.location.hash = 'shouldn\t match'; | ||
const result = auth.handleRedirect(); | ||
expect(result).toBe('NO_HASH_MATCH'); | ||
}); | ||
// const result = auth.handleRedirect(); | ||
// expect(result).toBe('NO_HASH_MATCH'); | ||
// }); | ||
it('calls `afterResponse` with the `decodeUriToObject`', () => { | ||
@@ -706,3 +767,3 @@ const storage = createTestStorage(); | ||
afterResponseCalled = true; | ||
} | ||
}, | ||
}; | ||
@@ -771,3 +832,3 @@ | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -789,3 +850,3 @@ storage._storage.token = exampleToken; | ||
}); | ||
it('doesn\'t call `beforePopup` if it doesn\'t exist', async () => { | ||
it("doesn't call `beforePopup` if it doesn't exist", async () => { | ||
const storage = createTestStorage(); | ||
@@ -874,5 +935,5 @@ | ||
return { | ||
foo: 'bar' | ||
foo: 'bar', | ||
}; | ||
} | ||
}, | ||
}; | ||
@@ -908,6 +969,7 @@ | ||
}); | ||
it('returns `SUCCESS` and calls `close` on the popup', async () => { | ||
it('returns `SUCCESS` and calls `close` on the popup and fires and event', async () => { | ||
const storage = createTestStorage(); | ||
let closedCalled = false; | ||
const eventCalled = new DeferredPromise(); | ||
@@ -936,9 +998,10 @@ (window as any).open = () => ({ | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
auth.addEventListener('login', eventCalled.resolve); | ||
OAuth2PopupFlow.time(0).then(() => { | ||
storage._storage.token = exampleToken; | ||
}) | ||
}); | ||
@@ -948,2 +1011,3 @@ expect(auth.loggedIn()).toBe(false); | ||
expect(closedCalled).toBe(true); | ||
await eventCalled; | ||
}); | ||
@@ -972,3 +1036,3 @@ }); | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -1009,3 +1073,3 @@ | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -1070,3 +1134,3 @@ | ||
window.btoa(JSON.stringify(examplePayload)), | ||
'this is the signature section' | ||
'this is the signature section', | ||
].join('.'); | ||
@@ -1108,2 +1172,115 @@ | ||
}); | ||
describe('EventTarget', () => { | ||
it('allows events to be listened to and dispatched', () => { | ||
const storage = createTestStorage(); | ||
const options = { | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: '', | ||
scope: 'openid profile', | ||
storage, | ||
}; | ||
const handler = jest.fn(); | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
auth.addEventListener('login', handler); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
expect(handler).toBeCalledTimes(3); | ||
}); | ||
it('allows event listeners to be removed', () => { | ||
const storage = createTestStorage(); | ||
const handler = jest.fn(); | ||
const options = { | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: '', | ||
scope: 'openid profile', | ||
storage, | ||
}; | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
auth.addEventListener('login', handler); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.removeEventListener('login', handler); | ||
auth.dispatchEvent(new Event('login')); | ||
expect(handler).toBeCalledTimes(3); | ||
}); | ||
it("doesn't throw when the type of event doesn't exist", () => { | ||
const storage = createTestStorage(); | ||
const options = { | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: '', | ||
scope: 'openid profile', | ||
storage, | ||
}; | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
auth.removeEventListener('login', () => {}); | ||
}); | ||
it('allows for an event handler object', () => { | ||
const storage = createTestStorage(); | ||
const options = { | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: '', | ||
scope: 'openid profile', | ||
storage, | ||
}; | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
const handler = jest.fn(); | ||
const eventListenerObject = { | ||
handleEvent: handler, | ||
}; | ||
auth.addEventListener('login', eventListenerObject); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
expect(handler).toBeCalledTimes(3); | ||
}); | ||
it('defaults to a no-op when there is nothing callable', () => { | ||
const storage = createTestStorage(); | ||
const options = { | ||
authorizationUri: 'http://example.com/oauth/authorize', | ||
clientId: 'some_test_client', | ||
redirectUri: '', | ||
scope: 'openid profile', | ||
storage, | ||
}; | ||
const auth = new OAuth2PopupFlow<ExampleTokenPayload>(options); | ||
const handler = jest.fn(); | ||
const notRealObj = {}; | ||
auth.addEventListener('login', notRealObj as any); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
auth.dispatchEvent(new Event('login')); | ||
expect(handler).toBeCalledTimes(0); | ||
}); | ||
}); | ||
}); |
234
src/index.ts
/** | ||
* The type of the configuration object used to create a `OAuth2PopupFlow` | ||
* | ||
* | ||
* Each property has a JSDOC description to explain what it does. | ||
@@ -10,10 +10,10 @@ */ | ||
* The full URI of the authorization endpoint provided by the authorization server. | ||
* | ||
* | ||
* e.g. `https://example.com/oauth/authorize` | ||
*/ | ||
authorizationUri: string, | ||
authorizationUri: string; | ||
/** | ||
* REQUIRED | ||
* The client ID of your application provided by the authorization server. | ||
* | ||
* | ||
* This client ID is sent to the authorization server using `authorizationUrl` endpoint in the | ||
@@ -25,3 +25,3 @@ * query portion of the URL along with the other parameters. | ||
*/ | ||
clientId: string, | ||
clientId: string; | ||
/** | ||
@@ -32,7 +32,7 @@ * REQUIRED | ||
* the authorization server. Some authorities call this a "callback URLs" or "login URLs" etc. | ||
* | ||
* | ||
* > e.g. `http://localhost:4200/redirect` for local testing | ||
* > | ||
* > | ||
* > or `https://my-application.com/redirect` for prod | ||
* | ||
* | ||
* This redirect URI is sent to the authorization server using `authorizationUrl` endpoint in the | ||
@@ -44,3 +44,3 @@ * query portion of the URL along with the other parameters. | ||
*/ | ||
redirectUri: string, | ||
redirectUri: string; | ||
/** | ||
@@ -51,5 +51,5 @@ * REQUIRED | ||
* them to approve those permission before authorizing your application. | ||
* | ||
* | ||
* > e.g. `openid profile` | ||
* | ||
* | ||
* The scopes are sent to the authorization server using `authorizationUrl` endpoint in the | ||
@@ -61,3 +61,3 @@ * query portion of the URL along with the other parameters. | ||
*/ | ||
scope: string, | ||
scope: string; | ||
/** | ||
@@ -67,9 +67,9 @@ * OPTIONAL | ||
* `authorizationUri` endpoint in the query portion of the URL. | ||
* | ||
* | ||
* Most implementations of oauth2 use the default value of `token` to tell the authorization | ||
* server to start the implicit grant flow but you may override that value with this option. | ||
* | ||
* | ||
* For example, Auth0--an OAuth2 authority/authorization server--requires the value `id_token` | ||
* instead of `token` for the implicit flow. | ||
* | ||
* | ||
* The response type is sent to the authorization server using `authorizationUrl` endpoint in the | ||
@@ -81,3 +81,3 @@ * query portion of the URL along with the other parameters. | ||
*/ | ||
responseType?: string, | ||
responseType?: string; | ||
/** | ||
@@ -89,3 +89,3 @@ * OPTIONAL | ||
*/ | ||
accessTokenStorageKey?: string, | ||
accessTokenStorageKey?: string; | ||
/** | ||
@@ -96,6 +96,6 @@ * OPTIONAL | ||
* determines the key to use that will retrieve the token from that object. | ||
* | ||
* | ||
* By default it is `access_token` but you you may need to change that e.g. Auth0 uses `id_token`. | ||
*/ | ||
accessTokenResponseKey?: string, | ||
accessTokenResponseKey?: string; | ||
/** | ||
@@ -106,3 +106,3 @@ * OPTIONAL | ||
*/ | ||
storage?: Storage, | ||
storage?: Storage; | ||
/** | ||
@@ -112,6 +112,6 @@ * OPTIONAL | ||
* returns `true`. | ||
* | ||
* | ||
* This property is how long it will wait between checks. By default it is `200`. | ||
*/ | ||
pollingTime?: number, | ||
pollingTime?: number; | ||
/** | ||
@@ -121,7 +121,7 @@ * OPTIONAL | ||
* URL in order for the implicit grant flow to work. | ||
* | ||
* | ||
* For example: [Auth0--an OAuth2 authority/authorization server--requires the parameters | ||
* `nonce`][0] | ||
* be passed along with every call to the `authorizationUri`. You can do that like so: | ||
* | ||
* | ||
* ```ts | ||
@@ -143,3 +143,3 @@ * const auth = new OAuth2PopupFlow({ | ||
* // the token returned by Auth0, has the `nonce` in the payload | ||
* // you can add this additional check now | ||
* // you can add this additional check now | ||
* tokenValidator: ({ payload }) => { | ||
@@ -152,6 +152,6 @@ * const storageNonce = parseInt(localStorage.getItem('nonce'), 10); | ||
* ``` | ||
* | ||
* | ||
* [0]: https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce | ||
*/ | ||
additionalAuthorizationParameters?: (() => { [key: string]: string }) | { [key: string]: string }, | ||
additionalAuthorizationParameters?: (() => { [key: string]: string }) | { [key: string]: string }; | ||
/** | ||
@@ -162,5 +162,5 @@ * OPTIONAL | ||
* or token. | ||
* | ||
* | ||
* [For example: validating the `nonce`:][0] | ||
* | ||
* | ||
* ```ts | ||
@@ -190,6 +190,6 @@ * const auth = new OAuth2PopupFlow({ | ||
* ``` | ||
* | ||
* | ||
* [0]: https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce | ||
*/ | ||
tokenValidator?: (options: { payload: TokenPayload, token: string }) => boolean, | ||
tokenValidator?: (options: { payload: TokenPayload; token: string }) => boolean; | ||
/** | ||
@@ -199,17 +199,17 @@ * OPTIONAL | ||
* `Promise` and the popup will not open until it resolves. | ||
* | ||
* | ||
* A typical use case would be to wait a certain amount of time before opening the popup to let | ||
* the user see why the popup is happening. | ||
*/ | ||
beforePopup?: () => any | Promise<any>, | ||
beforePopup?: () => any | Promise<any>; | ||
/** | ||
* OPTIONAL | ||
* A hook that runs in `handleRedirect` that takes in the result of the hash payload from the | ||
* A hook that runs in `handleRedirect` that takes in the result of the hash payload from the | ||
* authorization server. Use this hook to grab more from the response or to debug the response | ||
* from the authorization URL. | ||
*/ | ||
afterResponse?: (authorizationResponse: { [key: string]: string | undefined }) => void, | ||
afterResponse?: (authorizationResponse: { [key: string]: string | undefined }) => void; | ||
} | ||
export class OAuth2PopupFlow<TokenPayload extends { exp: number }> { | ||
export class OAuth2PopupFlow<TokenPayload extends { exp: number }> implements EventTarget { | ||
authorizationUri: string; | ||
@@ -225,5 +225,8 @@ clientId: string; | ||
additionalAuthorizationParameters?: (() => { [key: string]: string }) | { [key: string]: string }; | ||
tokenValidator?: (options: { payload: TokenPayload, token: string }) => boolean; | ||
tokenValidator?: (options: { payload: TokenPayload; token: string }) => boolean; | ||
beforePopup?: () => any | Promise<any>; | ||
afterResponse?: (authorizationResponse: { [key: string]: string | undefined }) => void; | ||
private _eventListeners: { | ||
[type: string]: EventListenerOrEventListenerObject[]; | ||
}; | ||
@@ -244,2 +247,3 @@ constructor(options: OAuth2PopupFlowOptions<TokenPayload>) { | ||
this.afterResponse = options.afterResponse; | ||
this._eventListeners = {}; | ||
} | ||
@@ -251,4 +255,5 @@ | ||
private set _rawToken(value: string | undefined) { | ||
if (value === null) { return; } | ||
if (value === undefined) { return; } | ||
if (value === null) return; | ||
if (value === undefined) return; | ||
this.storage.setItem(this.accessTokenStorageKey, value); | ||
@@ -259,12 +264,10 @@ } | ||
const rawToken = this._rawToken; | ||
if (!rawToken) { return undefined; } | ||
if (!rawToken) return undefined; | ||
const tokenSplit = rawToken.split('.'); | ||
const encodedPayload = tokenSplit[1]; | ||
if (!encodedPayload) { return undefined; } | ||
if (!encodedPayload) return undefined; | ||
const decodedPayloadJson = window.atob(encodedPayload); | ||
const decodedPayload = OAuth2PopupFlow.jsonParseOrUndefined<TokenPayload>( | ||
decodedPayloadJson | ||
); | ||
const decodedPayload = OAuth2PopupFlow.jsonParseOrUndefined<TokenPayload>(decodedPayloadJson); | ||
return decodedPayload; | ||
@@ -279,14 +282,13 @@ } | ||
const decodedPayload = this._rawTokenPayload; | ||
if (!decodedPayload) { return false; } | ||
if (!decodedPayload) return false; | ||
if (this.tokenValidator) { | ||
const token = this._rawToken!; | ||
if (!this.tokenValidator({ payload: decodedPayload, token })) { return false; } | ||
if (!this.tokenValidator({ payload: decodedPayload, token })) return false; | ||
} | ||
const exp = decodedPayload.exp; | ||
if (!exp) { return false; } | ||
if (!exp) return false; | ||
if (new Date().getTime() > (exp * 1000)) { return false; } | ||
if (new Date().getTime() > exp * 1000) return false; | ||
return true; | ||
@@ -302,8 +304,8 @@ } | ||
const decodedPayload = this._rawTokenPayload; | ||
if (!decodedPayload) { return false; } | ||
if (!decodedPayload) return false; | ||
const exp = decodedPayload.exp; | ||
if (!exp) { return false; } | ||
if (!exp) return false; | ||
if (new Date().getTime() <= (exp * 1000)) { return false; } | ||
if (new Date().getTime() <= exp * 1000) return false; | ||
@@ -315,5 +317,7 @@ return true; | ||
* Deletes the token from the given storage causing `loggedIn` to return false on its next call. | ||
* Also dispatches `logout` event | ||
*/ | ||
logout() { | ||
this.storage.removeItem(this.accessTokenStorageKey); | ||
this.dispatchEvent(new Event('logout')); | ||
} | ||
@@ -324,3 +328,3 @@ | ||
* `window.location.hash` and attempts to grab the token from the URL. | ||
* | ||
* | ||
* If the method was able to grab the token, it will return `'SUCCESS'` else it will return a | ||
@@ -331,7 +335,10 @@ * different string. | ||
const locationHref = window.location.href; | ||
if (!locationHref.startsWith(this.redirectUri)) { return 'REDIRECT_URI_MISMATCH'; } | ||
if (!locationHref.startsWith(this.redirectUri)) return 'REDIRECT_URI_MISMATCH'; | ||
const rawHash = window.location.hash; | ||
if (!rawHash) { return 'FALSY_HASH'; } | ||
if (!rawHash) return 'FALSY_HASH'; | ||
const hashMatch = /#(.*)/.exec(rawHash); | ||
if (!hashMatch) { return 'NO_HASH_MATCH'; } | ||
// this case won't happen because the browser typically adds the `#` always | ||
if (!hashMatch) return 'NO_HASH_MATCH'; | ||
const hash = hashMatch[1]; | ||
@@ -344,3 +351,4 @@ | ||
const rawToken = authorizationResponse[this.accessTokenResponseKey]; | ||
if (!rawToken) { return 'FALSY_TOKEN'; } | ||
if (!rawToken) return 'FALSY_TOKEN'; | ||
this._rawToken = rawToken; | ||
@@ -352,2 +360,40 @@ window.location.hash = ''; | ||
/** | ||
* supported events are: | ||
* | ||
* 1. `logout`–fired when the `logout()` method is called and | ||
* 2. `login`–fired during the `tryLoginPopup()` method is called and succeeds | ||
*/ | ||
addEventListener(type: string, listener: EventListenerOrEventListenerObject) { | ||
const listeners = this._eventListeners[type] || []; | ||
listeners.push(listener); | ||
this._eventListeners[type] = listeners; | ||
} | ||
/** | ||
* Use this to dispatch an event to the internal `EventTarget` | ||
*/ | ||
dispatchEvent(event: Event) { | ||
const listeners = this._eventListeners[event.type] || []; | ||
for (const listener of listeners) { | ||
const dispatch = | ||
typeof listener === 'function' | ||
? listener | ||
: typeof listener === 'object' && typeof listener.handleEvent === 'function' | ||
? listener.handleEvent.bind(listener) | ||
: () => {}; | ||
dispatch(event); | ||
} | ||
return true; | ||
} | ||
/** | ||
* Removes the event listener in target's event listener list with the same type, callback, and options. | ||
*/ | ||
removeEventListener(type: string, listener: EventListenerOrEventListenerObject) { | ||
const listeners = this._eventListeners[type] || []; | ||
this._eventListeners[type] = listeners.filter(l => l !== listener); | ||
} | ||
/** | ||
* Tries to open a popup to login the user in. If the user is already `loggedIn()` it will | ||
@@ -357,5 +403,7 @@ * immediately return `'ALREADY_LOGGED_IN'`. If the popup fails to open, it will immediately | ||
* return `'SUCCESS'`. | ||
* | ||
* Also dispatches `login` event | ||
*/ | ||
async tryLoginPopup() { | ||
if (this.loggedIn()) { return 'ALREADY_LOGGED_IN'; } | ||
if (this.loggedIn()) return 'ALREADY_LOGGED_IN'; | ||
@@ -366,21 +414,23 @@ if (this.beforePopup) { | ||
const additionalParams = (/*if*/ typeof this.additionalAuthorizationParameters === 'function' | ||
? this.additionalAuthorizationParameters() | ||
: /*if*/ typeof this.additionalAuthorizationParameters === 'object' | ||
const additionalParams = | ||
typeof this.additionalAuthorizationParameters === 'function' | ||
? this.additionalAuthorizationParameters() | ||
: typeof this.additionalAuthorizationParameters === 'object' | ||
? this.additionalAuthorizationParameters | ||
: {} | ||
: {}; | ||
const popup = window.open( | ||
`${this.authorizationUri}?${OAuth2PopupFlow.encodeObjectToUri({ | ||
client_id: this.clientId, | ||
response_type: this.responseType, | ||
redirect_uri: this.redirectUri, | ||
scope: this.scope, | ||
...additionalParams, | ||
})}`, | ||
); | ||
if (!popup) return 'POPUP_FAILED'; | ||
const popup = window.open(`${this.authorizationUri}?${OAuth2PopupFlow.encodeObjectToUri({ | ||
client_id: this.clientId, | ||
response_type: this.responseType, | ||
redirect_uri: this.redirectUri, | ||
scope: this.scope, | ||
...additionalParams, | ||
})}`); | ||
if (!popup) { return 'POPUP_FAILED'; } | ||
await this.authenticated(); | ||
popup.close(); | ||
this.dispatchEvent(new Event('login')); | ||
@@ -407,5 +457,3 @@ return 'SUCCESS'; | ||
const token = this._rawToken; | ||
if (!token) { | ||
throw new Error('Token was falsy after being authenticated.'); | ||
} | ||
if (!token) throw new Error('Token was falsy after being authenticated.'); | ||
return token; | ||
@@ -421,5 +469,3 @@ } | ||
const payload = this._rawTokenPayload; | ||
if (!payload) { | ||
throw new Error('Token payload was falsy after being authenticated.'); | ||
} | ||
if (!payload) throw new Error('Token payload was falsy after being authenticated.'); | ||
return payload; | ||
@@ -443,6 +489,3 @@ } | ||
static time(milliseconds: number) { | ||
return new Promise<'TIMER'>(resolve => window.setTimeout( | ||
() => resolve('TIMER'), | ||
milliseconds | ||
)); | ||
return new Promise<'TIMER'>(resolve => window.setTimeout(() => resolve('TIMER'), milliseconds)); | ||
} | ||
@@ -463,12 +506,10 @@ | ||
* Encodes an object of strings to a URL | ||
* | ||
* | ||
* `{one: 'two', buckle: 'shoes or something'}` ==> `one=two&buckle=shoes%20or%20something` | ||
*/ | ||
static encodeObjectToUri(obj: { [key: string]: string }) { | ||
return (Object | ||
.keys(obj) | ||
return Object.keys(obj) | ||
.map(key => ({ key, value: obj[key] })) | ||
.map(({ key, value }) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) | ||
.join('&') | ||
); | ||
.join('&'); | ||
} | ||
@@ -478,14 +519,17 @@ | ||
* Decodes a URL string to an object of string | ||
* | ||
* | ||
* `one=two&buckle=shoes%20or%20something` ==> `{one: 'two', buckle: 'shoes or something'}` | ||
*/ | ||
static decodeUriToObject(str: string) { | ||
return str.split('&').reduce((decoded, keyValuePair) => { | ||
const [keyEncoded, valueEncoded] = keyValuePair.split('='); | ||
const key = this.decodeUri(keyEncoded); | ||
const value = this.decodeUri(valueEncoded); | ||
decoded[key] = value; | ||
return decoded; | ||
}, {} as { [key: string]: string | undefined }); | ||
return str.split('&').reduce( | ||
(decoded, keyValuePair) => { | ||
const [keyEncoded, valueEncoded] = keyValuePair.split('='); | ||
const key = this.decodeUri(keyEncoded); | ||
const value = this.decodeUri(valueEncoded); | ||
decoded[key] = value; | ||
return decoded; | ||
}, | ||
{} as { [key: string]: string | undefined }, | ||
); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
219218
10
19
2085
0