Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

oauth2-popup-flow

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

oauth2-popup-flow - npm Package Compare versions

Comparing version 0.1.0 to 0.1.1

coverage/clover.xml

2

dist/oauth2-popup-flow.js

@@ -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);
});
});
});
/**
* 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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc