New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@cimpress/simple-auth-wrapper

Package Overview
Dependencies
Maintainers
9
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cimpress/simple-auth-wrapper - npm Package Compare versions

Comparing version 6.2.1 to 6.3.0

6

CHANGELOG.md
# Changelog
## 6.3 (2018-08-28)
The `tokenExpired` event passes the expiration time to the listening callback.
Added `authenticated` subscribable event - e.g. `auth.on('authenticated', () => ...do something)`. See CentralizedAuth source for documentation.
CentralizedAuth now also listens for StorageEvents that are emitted upon changes to localStorage. This means that if multiple tabs/windows are open of the same origin, events will trigger when signing into one of these copies to help keep the state in sync. See source for more detailed documentation.
## 6.2 (2018-07-11)

@@ -4,0 +10,0 @@

103

lib/centralizedauth.js

@@ -13,5 +13,5 @@ 'use strict';

var _events2 = require('events');
var _events3 = require('events');
var _events3 = _interopRequireDefault(_events2);
var _events4 = _interopRequireDefault(_events3);

@@ -49,2 +49,5 @@ var _lodash = require('lodash.merge');

// Emit the "authenticated" event if the token is not expired at the time of client subscription (e.g. auth.on('authenticated', ...))
emitInitialAuthenticated: true,
// number of seconds of clock skew to tolerate (see https://auth0.com/docs/libraries/lock/v10/customization#leeway-integer-)

@@ -63,2 +66,3 @@ leeway: 30

// emitInitialTokenExpired (optional, see default above)
// emitInitialAuthenticated (optional, see default above)
// leeway (optional, see default above)

@@ -84,3 +88,6 @@ // lockWidgetOptions (optional, see defaults in newLockWidget)

this.events = new _events3.default();
this.events = new _events4.default();
// listen for changes to localStorage that don't originate from this current window/tab
window.addEventListener('storage', this.listenToStorage);
}

@@ -90,8 +97,26 @@

// Subscribable event types:
// 1. tokenExpired
// 1. tokenExpired: (expirationTime) =>
// * Fired when the amount of time the token is good for (minus the "expirationOffset") has elapsed
// * Fired if there exists an expired token in localStorage at the time of subscription
// - (configurable via "emitInitialTokenExpired" option)
// 2. logout:
// * Fired when user is logged out
// 3. authenticated: (source) =>
// * Fired if there exists an unexpired token in localStorage at the time of subscription
// - (configurable via "emitInitialAuthenticated" option)
// - source will be === "initial" in the provided callback for this scenario
// - it is also implied that the current window/tab is the source of the event
// * Fired when "this" instance of the authWrapper saves the user's login information to localStorage
// - source will be === "this" to denote that the event is coming from this instance
// * If the current window/tab does not have focus (i.e. blurred), this is fired when a new expiration is set
// in localStorage from a different window/tab with the same origin (e.g. 2 copies of the same app and you
// sign-in to one)
// - source will be === "storage" in the provided callback to denote that the authentication
// happened in a different window/tab
// It is possible to create an infinite loop between 2 tabs/windows if localStorage is modified
// within this listener - so don't do it :)
// Check whether the current time is past the access token's expiry time.

@@ -140,7 +165,53 @@ // This method ignores the expiration offset option.

if (expiration && expiration - _this.expirationOffset * 1000 <= new Date().getTime()) {
_this.events.emit('tokenExpired');
_this.events.emit('tokenExpired', expiration);
}
}
if (eventType === 'authenticated' && _this.emitInitialAuthenticated) {
if (_this.isLoggedIn()) {
_this.events.emit('authenticated', 'initial');
}
}
};
this.removeListener = function (eventType) {
var _events2;
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
(_events2 = _this.events).removeListener.apply(_events2, [eventType].concat(args));
};
this.setExpirationTimer = function (expiresAt) {
_this.clearTimeout();
var timeToWait = expiresAt - new Date().getTime() - 1000 * _this.expirationOffset;
_this.expiresTimeout = window.setTimeout(function () {
return _this.events.emit('tokenExpired', expiresAt);
}, timeToWait);
};
this.listenToStorage = function (e) {
switch (e.key) {
// TODO: namespace the key (e.g. "expires_at" -> "saw:expires_at") we are using since there is no way to tell
// if this event is coming from localStorage or sessionStorage - if applications are using "expires_at" in
// sessionStorage, this will result in undefined behavior
case 'expires_at':
// check to see if it's being removed or not
if (e.newValue) {
try {
var expiresAt = JSON.parse(e.newValue);
_this.setExpirationTimer(expiresAt);
_this.events.emit('authenticated', 'storage');
} catch (e) {
// for JSON.parse: shouldn't happen unless people are modifying localStorage manually or using
// the "expires_at" key in sessionStorage
}
}
break;
default:
}
};
this.isLoggedIn = function () {

@@ -156,14 +227,17 @@ try {

this.saveToken = function (token, accessToken, profile, expiresIn) {
localStorage.setItem('token', token);
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('profile', JSON.stringify(profile));
if (expiresIn) {
// Set the time that the access token will expire at
var expiresAt = JSON.stringify(expiresIn * 1000 + new Date().getTime());
localStorage.setItem('expires_at', expiresAt);
_this.clearTimeout();
_this.expiresTimeout = window.setTimeout(function () {
return _this.events.emit('tokenExpired');
}, (expiresIn - _this.expirationOffset) * 1000);
var expiresAt = expiresIn * 1000 + new Date().getTime();
// This will trigger listenToStorage on "blurred" tabs/windows of the same origin
// It will not trigger this.listenToStorage in the current tab/window because that's not how StorageEvent's work
localStorage.setItem('expires_at', JSON.stringify(expiresAt));
// Which is why we have to explicitly set the timer instead of relying on the localStorage listener
_this.setExpirationTimer(expiresAt);
_this.events.emit('authenticated', 'this');
}
localStorage.setItem('token', token);
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('profile', JSON.stringify(profile));
};

@@ -319,2 +393,3 @@

_this.removeToken();
_this.events.emit('logout');
// also log out with auth0

@@ -321,0 +396,0 @@ var returnTo = nextUri ? { returnTo: window.location.origin + nextUri } : {};

10

package.json
{
"name": "@cimpress/simple-auth-wrapper",
"version": "6.2.1",
"version": "6.3.0",
"description": "A simple utility class to wrap basic Auth0 functionality",

@@ -33,11 +33,5 @@ "main": "lib/index.js",

"gulp-sourcemaps": "2.6.1",
"jest": "^22.4.3",
"jest-localstorage-mock": "^2.2.0",
"jest": "^23.5.0",
"jest-plugin-clock": "^2.9.0"
},
"jest": {
"setupFiles": [
"jest-localstorage-mock"
]
}
}

@@ -27,2 +27,5 @@ import Auth0 from 'auth0-js';

// Emit the "authenticated" event if the token is not expired at the time of client subscription (e.g. auth.on('authenticated', ...))
emitInitialAuthenticated: true,
// number of seconds of clock skew to tolerate (see https://auth0.com/docs/libraries/lock/v10/customization#leeway-integer-)

@@ -41,2 +44,3 @@ leeway: 30

// emitInitialTokenExpired (optional, see default above)
// emitInitialAuthenticated (optional, see default above)
// leeway (optional, see default above)

@@ -59,2 +63,5 @@ // lockWidgetOptions (optional, see defaults in newLockWidget)

this.events = new EventEmitter();
// listen for changes to localStorage that don't originate from this current window/tab
window.addEventListener('storage', this.listenToStorage);
}

@@ -64,6 +71,20 @@

// Subscribable event types:
// 1. tokenExpired
// 1. tokenExpired: (expirationTime) =>
// * Fired when the amount of time the token is good for (minus the "expirationOffset") has elapsed
// * Fired if there exists an expired token in localStorage at the time of subscription
// - (configurable via "emitInitialTokenExpired" option)
// 2. logout:
// * Fired when user is logged out
// 3. authenticated: (source) =>
// * Fired if there exists an unexpired token in localStorage at the time of subscription
// - (configurable via "emitInitialAuthenticated" option)
// - source will be === "initial" in the provided callback for this scenario
// - it is also implied that the current window/tab is the source of the event
// * Fired when "this" instance of the authWrapper saves the user's login information to localStorage
// - source will be === "this" to denote that the event is coming from this instance
// * If the current window/tab does not have focus (i.e. blurred), this is fired when a new expiration is set
// in localStorage from a different window/tab with the same origin (e.g. 2 copies of the same app and you
// sign-in to one)
// - source will be === "storage" in the provided callback to denote that the authentication
// happened in a different window/tab
on = (eventType, ...args) => {

@@ -75,7 +96,47 @@ this.events.on(eventType, ...args);

if (expiration && expiration - (this.expirationOffset * 1000) <= new Date().getTime()) {
this.events.emit('tokenExpired');
this.events.emit('tokenExpired', expiration);
}
}
if (eventType === 'authenticated' && this.emitInitialAuthenticated) {
if (this.isLoggedIn()) {
this.events.emit('authenticated', 'initial');
}
}
};
removeListener = (eventType, ...args) => {
this.events.removeListener(eventType, ...args);
};
setExpirationTimer = (expiresAt) => {
this.clearTimeout();
const timeToWait = expiresAt - new Date().getTime() - (1000 * this.expirationOffset);
this.expiresTimeout = window.setTimeout(() => this.events.emit('tokenExpired', expiresAt), timeToWait);
};
// It is possible to create an infinite loop between 2 tabs/windows if localStorage is modified
// within this listener - so don't do it :)
listenToStorage = (e) => {
switch(e.key) {
// TODO: namespace the key (e.g. "expires_at" -> "saw:expires_at") we are using since there is no way to tell
// if this event is coming from localStorage or sessionStorage - if applications are using "expires_at" in
// sessionStorage, this will result in undefined behavior
case 'expires_at':
// check to see if it's being removed or not
if (e.newValue) {
try {
const expiresAt = JSON.parse(e.newValue);
this.setExpirationTimer(expiresAt);
this.events.emit('authenticated', 'storage');
} catch (e) {
// for JSON.parse: shouldn't happen unless people are modifying localStorage manually or using
// the "expires_at" key in sessionStorage
}
}
break;
default:
}
};
// Check whether the current time is past the access token's expiry time.

@@ -93,12 +154,17 @@ // This method ignores the expiration offset option.

saveToken = (token, accessToken, profile, expiresIn) => {
localStorage.setItem('token', token);
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('profile', JSON.stringify(profile));
if (expiresIn) {
// Set the time that the access token will expire at
const expiresAt = JSON.stringify(expiresIn * 1000 + new Date().getTime());
localStorage.setItem('expires_at', expiresAt);
this.clearTimeout();
this.expiresTimeout = window.setTimeout(() => this.events.emit('tokenExpired'), (expiresIn - this.expirationOffset) * 1000);
const expiresAt = expiresIn * 1000 + new Date().getTime();
// This will trigger listenToStorage on "blurred" tabs/windows of the same origin
// It will not trigger this.listenToStorage in the current tab/window because that's not how StorageEvent's work
localStorage.setItem('expires_at', JSON.stringify(expiresAt));
// Which is why we have to explicitly set the timer instead of relying on the localStorage listener
this.setExpirationTimer(expiresAt);
this.events.emit('authenticated', 'this');
}
localStorage.setItem('token', token);
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('profile', JSON.stringify(profile));
};

@@ -170,3 +236,3 @@

});
}
};

@@ -250,2 +316,3 @@ // Checks whether the app is authenticated. If not it automatically initiates authentication.

this.removeToken();
this.events.emit('logout');
// also log out with auth0

@@ -252,0 +319,0 @@ const returnTo = nextUri ? {returnTo: window.location.origin + nextUri} : {};

@@ -12,6 +12,6 @@ import clock from 'jest-plugin-clock';

beforeEach(() => {
localStorage.clear();
localStorage.setItem('token', fakeToken);
localStorage.setItem('accessToken', fakeAccessToken);
localStorage.setItem('profile', JSON.stringify(fakeProfile));
window.localStorage.clear();
window.localStorage.setItem('token', fakeToken);
window.localStorage.setItem('accessToken', fakeAccessToken);
window.localStorage.setItem('profile', JSON.stringify(fakeProfile));
clock.set(now); // make every new Date() equal to current time

@@ -39,3 +39,3 @@ jest.useFakeTimers();

test('should return undefined if profile is not in localStorage', () => {
localStorage.removeItem('profile');
window.localStorage.removeItem('profile');
expect(testModule.getProfile()).toBeUndefined();

@@ -51,5 +51,5 @@ });

testModule.saveToken(fakeToken2, fakeAccessToken2, fakeProfile, 10);
const token = localStorage.getItem('token');
const accessToken = localStorage.getItem('accessToken');
const expires = JSON.parse(localStorage.getItem('expires_at'));
const token = window.localStorage.getItem('token');
const accessToken = window.localStorage.getItem('accessToken');
const expires = JSON.parse(window.localStorage.getItem('expires_at'));

@@ -71,2 +71,18 @@ expect(token).toBe(fakeToken2);

describe('and the subscription to the event is removed', () => {
beforeEach(() => {
testModule.removeListener('tokenExpired', tokenExpiredCallback);
});
describe('and the "tokenExpired" event is emitted', () => {
beforeEach(() => {
testModule.events.emit('tokenExpired', now.getTime());
});
test('the callback should not be called', () => {
expect(tokenExpiredCallback).not.toHaveBeenCalled();
});
});
});
describe('and the user gets a new token set to expire 60 seconds in the future', () => {

@@ -85,2 +101,3 @@ beforeEach(() => {

expect(tokenExpiredCallback).toHaveBeenCalledTimes(1);
expect(tokenExpiredCallback).toBeCalledWith(now.getTime() + 60000);
});

@@ -101,8 +118,59 @@ });

describe('when the client has subscribed to the "authenticated" event', () => {
let authenticatedCallback;
beforeEach(() => {
authenticatedCallback = jest.fn();
testModule.on('authenticated', authenticatedCallback);
});
describe('and someone signs into the app from another tab', () => {
let storageEvent;
beforeEach(() => {
const newExpiration = (now.getTime() + 60 * 60 * 4 * 1000).toString(); // 4 hours from now
window.localStorage.setItem('expires_at', newExpiration); // this does not trigger the StorageEvent
storageEvent = new window.StorageEvent('storage', {
key: 'expires_at',
oldValue: now.getTime().toString(),
newValue: newExpiration
});
window.dispatchEvent(storageEvent); // this triggers the StorageEvent
});
test('the callback should be called', () => {
expect(authenticatedCallback).toHaveBeenCalledTimes(1);
expect(authenticatedCallback).toHaveBeenCalledWith('storage');
})
});
describe('and the client unsubscribes from the event', () => {
beforeEach(() => {
testModule.removeListener('authenticated', authenticatedCallback);
});
describe('and the authenticated event is emitted', () => {
beforeEach(() => {
testModule.events.emit('authenticated');
});
test('the callback should not be called', () => {
expect(authenticatedCallback).not.toHaveBeenCalled();
});
})
})
});
describe('when the user has a token that expires in 1ms', () => {
beforeEach(() => {
// set expiration 1 millisecond into future
localStorage.setItem('expires_at', JSON.stringify(now.getTime() + 1));
window.localStorage.setItem('expires_at', JSON.stringify(now.getTime() + 1));
});
test('subscribing to "authenticated" should fire the event', () => {
const authenticatedCallback = jest.fn();
testModule.on('authenticated', authenticatedCallback);
expect(authenticatedCallback).toHaveBeenCalledTimes(1);
expect(authenticatedCallback).toBeCalledWith('initial');
});
test('isLoggedIn should return true', () => {

@@ -116,3 +184,3 @@ expect(testModule.isLoggedIn()).toBe(true);

// set expiration 30 seconds into future (i.e. expirationOffset)
localStorage.setItem('expires_at', JSON.stringify(now.getTime() + 30000));
window.localStorage.setItem('expires_at', JSON.stringify(now.getTime() + 30000));
});

@@ -131,3 +199,3 @@

// set expiration to now
localStorage.setItem('expires_at', JSON.stringify(now.getTime()));
window.localStorage.setItem('expires_at', JSON.stringify(now.getTime()));
});

@@ -147,4 +215,6 @@

describe('and the user logs in', () => {
let loginPromise, fakeAuthResponse;
let loginPromise, fakeAuthResponse, authenticatedCallback;
beforeEach(() => {
authenticatedCallback = jest.fn();
testModule.on('authenticated', authenticatedCallback);
fakeAuthResponse = {

@@ -172,2 +242,4 @@ idToken: fakeToken + '2',

expect(expiration).toBe(now.getTime() + (fakeAuthResponse.expiresIn * 1000));
expect(authenticatedCallback).toHaveBeenCalledTimes(1);
expect(authenticatedCallback).toHaveBeenCalledWith('this');
});;

@@ -179,3 +251,3 @@ });

describe('Centralized Auth using emitInitialTokenExpired = false', () => {
describe('Centralized Auth using emitInitialTokenExpired = false, and emitInitialAuthenticated = false', () => {
let testModule;

@@ -185,9 +257,23 @@ beforeEach(() => {

clientID: 'fake-client-id',
emitInitialTokenExpired: false
emitInitialTokenExpired: false,
emitInitialAuthenticated: false
});
});
describe('when the token is not yet expired', () => {
beforeEach(() => {
window.localStorage.setItem('expires_at', JSON.stringify(now.getTime() + 1));
});
test('subscribing to "authenticated" should not fire the event', () => {
const authenticatedCallback = jest.fn();
testModule.on('authenticated', authenticatedCallback);
expect(authenticatedCallback).not.toHaveBeenCalled();
});
});
describe('when the token is expired', () => {
beforeEach(() => {
localStorage.setItem('expires_at', JSON.stringify(now.getTime()));
window.localStorage.setItem('expires_at', JSON.stringify(now.getTime()));
});

@@ -202,2 +288,2 @@

});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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