From 15aa787838a682af91417bba47920152f28467e6 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Tue, 29 Mar 2016 16:36:18 -0400 Subject: [PATCH 01/23] 1.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9f1e38..2ff8934 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.2.1", + "version": "1.2.2", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From 4a894f22dec0c3fdee26c94e5f9e98bee032e549 Mon Sep 17 00:00:00 2001 From: Elliot Shiu Date: Sat, 16 Apr 2016 01:24:09 +0000 Subject: [PATCH 02/23] expose refreshToken --- src/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.js b/src/index.js index d20dce7..1f8addc 100644 --- a/src/index.js +++ b/src/index.js @@ -72,5 +72,7 @@ module.exports = (options) => { setInterval(refreshToken, refreshInterval) + tokenStore.refreshToken = () => refreshToken() + return tokenStore } From 9b3edd3d6638488b7192a4e611d9d70be4507954 Mon Sep 17 00:00:00 2001 From: Elliot Shiu Date: Mon, 18 Apr 2016 16:28:06 -0700 Subject: [PATCH 03/23] lanetix/issues#5293: refresh token --- README.md | 6 ++++++ src/index.js | 7 +++++-- test/data/token-timezone.json | 1 + test/index.js | 22 +++++++++++++++++++++- 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/data/token-timezone.json diff --git a/README.md b/README.md index d880486..a233841 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ You can set the token without interacting with cookies via the following. userStore.setToken('jwt') ``` +### Refresh the token +You can force a refresh of the token via the following. +```javascript +userStore.refreshToken() +``` + ### Override Cookie Key By default, the JWT assumes the cookie key is `XSRF-TOKEN`. This can be overridden diff --git a/src/index.js b/src/index.js index 1f8addc..2914d16 100644 --- a/src/index.js +++ b/src/index.js @@ -47,6 +47,11 @@ module.exports = (options) => { token = newToken user = decodeToken(token) this.emit('Token received') + }, + + refreshToken () { + options.refresh() + .then(tokenStore.setToken.bind(tokenStore)) } }, EventEmitter.prototype) @@ -72,7 +77,5 @@ module.exports = (options) => { setInterval(refreshToken, refreshInterval) - tokenStore.refreshToken = () => refreshToken() - return tokenStore } diff --git a/test/data/token-timezone.json b/test/data/token-timezone.json new file mode 100644 index 0000000..42e1d7f --- /dev/null +++ b/test/data/token-timezone.json @@ -0,0 +1 @@ +"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6Mjc1MTA1NSwidXNlcl9pZCI6Mjc1MTA1NSwibG9naW5faWQiOjIzMSwib3JnYW5pemF0aW9uX2lkIjoyNzMxMTExLCJvcmdhbml6YXRpb25fbmFtZSI6IkRFVjogRGV2ZWxvcG1lbnQgRW52aXJvbm1lbnQiLCJlbWFpbCI6Im1pa2UuYXRraW5zQGxhbmV0aXguY29tIiwiZmlyc3RfbmFtZSI6Ik1pa2UiLCJsYXN0X25hbWUiOiJBdGtpbnMiLCJhdmF0YXJfdXJsIjoiaHR0cHM6Ly9zZWN1cmUubGFuZXRpeC5jb20vY3NzL2ltYWdlcy9kZWZhdWx0LWF2YXRhci5wbmciLCJwZXJtaXNzaW9ucyI6WyJsYW5ldGl4LXN0YWZmIiwiY3VzdG9tZXItYWRtaW5pc3RyYXRvciIsImFuYWx5dGljcyJdLCJ0aW1lem9uZSI6IlVUQyIsImZlYXR1cmVzIjp7fSwiaWF0IjoxNDI5NjUzNDI3LCJleHAiOjE0NjEwMTk0MTguNjQ5LCJhdWQiOiJ1cm46bGFuZXRpeC9hcGkiLCJpc3MiOiJ1cm46bGFuZXRpeC9hdXRoIiwic3ViIjoibWlrZS5hdGtpbnNAbGFuZXRpeC5jb20ifQ.kOAGDWy2lYSKTQSn2_jKSXJYRlPMJl8wWAJQSbgFQBM" diff --git a/test/index.js b/test/index.js index 6d883eb..6ef9e13 100644 --- a/test/index.js +++ b/test/index.js @@ -4,6 +4,7 @@ import assert from 'assert' import { btoa } from 'Base64' import decode from 'jwt-decode' import token from './data/token' +import tokenTimezone from './data/token-timezone' import ls from 'local-storage' import bluebird from 'bluebird' @@ -98,7 +99,7 @@ describe('Token Store', () => { assert.equal(user.last_name, 'Atkins') }) - it('it token to expire soon, refresh after interval', done => { + it('if token to expire soon, refresh after interval', done => { ls.set(localStorageKey, updatedToken) const tokenStore = require('../src')({ localStorageKey, @@ -114,6 +115,25 @@ describe('Token Store', () => { }) }) + it.only('refreshes the token and sets it', done => { + ls.set(localStorageKey, setTokenExp(Date.now() + 100 * 60 * 1000)) + const tokenStore = require('../src')({ + localStorageKey, + refresh: () => bluebird.resolve(tokenTimezone) + }) + + assert.equal(tokenStore.getUser().timezone, undefined) + + tokenStore.on('Token received', () => { + const user = tokenStore.getUser() + + assert.equal(user.timezone, 'UTC') + done() + }) + + tokenStore.refreshToken() + }) + describe('sad path', () => { it('should not blow up when cookie is not present', () => { let tokenStore From f0dafd6fae648b98ccb337c4b2d99b583998354c Mon Sep 17 00:00:00 2001 From: Elliot Shiu Date: Tue, 19 Apr 2016 00:26:30 +0000 Subject: [PATCH 04/23] lanetix/issues#5293: 100% coverage aww yeah --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 6ef9e13..28ca8bd 100644 --- a/test/index.js +++ b/test/index.js @@ -115,7 +115,7 @@ describe('Token Store', () => { }) }) - it.only('refreshes the token and sets it', done => { + it('refreshes the token and sets it', done => { ls.set(localStorageKey, setTokenExp(Date.now() + 100 * 60 * 1000)) const tokenStore = require('../src')({ localStorageKey, From 814ff8b40661382b473c11512d8503a74ef0ff9e Mon Sep 17 00:00:00 2001 From: Elliot Shiu Date: Tue, 19 Apr 2016 16:30:56 +0000 Subject: [PATCH 05/23] lanetix/issues#5293: dry up stuff --- src/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 2914d16..4506eb1 100644 --- a/src/index.js +++ b/src/index.js @@ -57,8 +57,7 @@ module.exports = (options) => { const refreshToken = () => { if (!token && options.refresh) { - options.refresh() - .then(tokenStore.setToken.bind(tokenStore)) + tokenStore.refreshToken() } else { user = decodeToken(token) } From f922fa89dd3d6d93d86fcdb9781e1c6fe507ef33 Mon Sep 17 00:00:00 2001 From: Elliot Shiu Date: Tue, 19 Apr 2016 18:35:17 +0000 Subject: [PATCH 06/23] 1.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ff8934..8beacf7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.2.2", + "version": "1.3.0", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From 2e314be40cd7e7ac8bc3f46c8772ae11e00ea247 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Wed, 20 Apr 2016 11:55:59 -0400 Subject: [PATCH 07/23] 1.3.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8beacf7..a5442c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.3.0", + "version": "1.3.1", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From 4af8c9ad9c611d405966dee6047f17de8a6a0799 Mon Sep 17 00:00:00 2001 From: Damir Tuka Date: Mon, 2 May 2016 14:12:48 -0700 Subject: [PATCH 08/23] lanetix/issues#5547 - Put getting localStorage token in a try/catch exception. The token will then refresh itself. --- src/index.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 4506eb1..062a129 100644 --- a/src/index.js +++ b/src/index.js @@ -24,11 +24,18 @@ const decodeToken = token => { module.exports = (options) => { options = extend({ cookie: 'XSRF-TOKEN' }, options) - let token = options.localStorageKey - ? ls.get(options.localStorageKey) - : cookie.get && cookie.get(options.cookie) - let user + let token + + if (options.localStorageKey) { + try { + token = ls.get(options.localStorageKey) + } catch(e) { + console.warn('Unable to get token', e) + } + } else { + token = cookie.get && cookie.get(options.cookie) + } const tokenStore = extend({ getToken () { From d26266e2113616c4cd20a0a5dd05d2e65d3cd299 Mon Sep 17 00:00:00 2001 From: Damir Tuka Date: Mon, 2 May 2016 14:15:12 -0700 Subject: [PATCH 09/23] Standard --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 062a129..5621312 100644 --- a/src/index.js +++ b/src/index.js @@ -30,7 +30,7 @@ module.exports = (options) => { if (options.localStorageKey) { try { token = ls.get(options.localStorageKey) - } catch(e) { + } catch (e) { console.warn('Unable to get token', e) } } else { From 0d1a9577990bbdbefa25e2756f036f942afc3707 Mon Sep 17 00:00:00 2001 From: Damir Tuka Date: Mon, 2 May 2016 14:23:40 -0700 Subject: [PATCH 10/23] Fix coverage --- test/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/index.js b/test/index.js index 28ca8bd..81b708b 100644 --- a/test/index.js +++ b/test/index.js @@ -57,6 +57,14 @@ describe('Token Store', () => { assert.equal(user.last_name, 'Atkins') }) + it('should catch an exception token is not present in local storage', () => { + ls.set(localStorageKey, undefined) + const tokenStore = require('../src')({localStorageKey}) + const token = tokenStore.getToken() + + assert.equal(token, undefined) + }) + it('if no token call refresh & set token', done => { const tokenStore = require('../src')({refresh: () => bluebird.resolve(updatedToken) From 76f121a7e9808a96ea58af676e913198f4ee45d0 Mon Sep 17 00:00:00 2001 From: Damir Tuka Date: Mon, 2 May 2016 14:30:23 -0700 Subject: [PATCH 11/23] 1.3.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a5442c1..97da625 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.3.1", + "version": "1.3.2", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From e7e71f3f6f9edd33163d73909341d7d70645d785 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Fri, 27 May 2016 13:32:17 -0700 Subject: [PATCH 12/23] Provide token to refresh --- src/index.js | 2 +- test/index.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 5621312..dad9cfa 100644 --- a/src/index.js +++ b/src/index.js @@ -71,7 +71,7 @@ module.exports = (options) => { let expDate = user ? new Date(user.exp * 1000 - 2 * refreshInterval) : null if (expDate && expDate < new Date() && options.refresh) { - options.refresh() + options.refresh(token) .then(tokenStore.setToken.bind(tokenStore)) } } diff --git a/test/index.js b/test/index.js index 81b708b..c5b8882 100644 --- a/test/index.js +++ b/test/index.js @@ -78,6 +78,18 @@ describe('Token Store', () => { }) }) + it('if token is expired, call refresh with expired token', done => { + ls.set(localStorageKey, token) + require('../src')({ + localStorageKey, + refresh: (t) => { + assert.equal(t, token) + done() + return bluebird.resolve(updatedToken) + } + }) + }) + it('if token is expired, call refresh & set token', done => { ls.set(localStorageKey, token) const tokenStore = require('../src')({ From 04a46993102dd63d885e37efbac52c1de3a620b0 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Fri, 27 May 2016 14:33:51 -0700 Subject: [PATCH 13/23] 1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 97da625..9d2ab9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.3.2", + "version": "1.4.0", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From cd365be9285216f747058c11466ba85edc596280 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Thu, 2 Jun 2016 10:30:08 -0700 Subject: [PATCH 14/23] DRY up token refresh --- src/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index dad9cfa..47cf362 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,7 @@ module.exports = (options) => { }, refreshToken () { - options.refresh() + options.refresh(token) .then(tokenStore.setToken.bind(tokenStore)) } }, EventEmitter.prototype) @@ -71,8 +71,7 @@ module.exports = (options) => { let expDate = user ? new Date(user.exp * 1000 - 2 * refreshInterval) : null if (expDate && expDate < new Date() && options.refresh) { - options.refresh(token) - .then(tokenStore.setToken.bind(tokenStore)) + tokenStore.refreshToken() } } From d730f34301e6e3ca1c63c3739e49b3d60991b56b Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Thu, 2 Jun 2016 13:25:53 -0700 Subject: [PATCH 15/23] Add logging --- README.md | 5 +++++ src/index.js | 39 +++++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a233841..cf84206 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,8 @@ by passing `cookie` on the `options` hash: ```javascript let userStore = require('react-jwt-store')({ cookie: 'NOT-XSRF-TOKEN'}); ``` + +### Set a logger + +By default, the store does not log anything, but if you pass in a `console` +compatible logger, the store will log the state of the token as it changes. diff --git a/src/index.js b/src/index.js index 47cf362..eeb9bf8 100644 --- a/src/index.js +++ b/src/index.js @@ -7,35 +7,40 @@ import events from 'events' import ls from 'local-storage' const EventEmitter = events.EventEmitter - -const canWarn = () => console && console.warn && typeof console.warn === 'function' - -const decodeToken = token => { - if (token) { - try { - return decode(token) - } catch (e) { - canWarn() && console.warn(`Invalid JWT: ${token}`) - return void 0 - } - } -} +const noop = function () { } module.exports = (options) => { - options = extend({ cookie: 'XSRF-TOKEN' }, options) - let user let token + options = extend({ cookie: 'XSRF-TOKEN' }, options) + + const logger = options.logger || { + info: noop, + warn: noop + } + + const decodeToken = token => { + if (token) { + try { + return decode(token) + } catch (e) { + logger.warn(`[JWT store] Invalid JWT: ${token}`) + return void 0 + } + } + } + if (options.localStorageKey) { try { token = ls.get(options.localStorageKey) } catch (e) { - console.warn('Unable to get token', e) + logger.warn('[JWT store] Unable to get token', e) } } else { token = cookie.get && cookie.get(options.cookie) } + logger.info('[JWT store] created store with token', token) const tokenStore = extend({ getToken () { @@ -51,12 +56,14 @@ module.exports = (options) => { }, setToken (newToken) { + logger.info('[JWT store] setting new token', newToken) token = newToken user = decodeToken(token) this.emit('Token received') }, refreshToken () { + logger.info('[JWT store] refreshing token', token) options.refresh(token) .then(tokenStore.setToken.bind(tokenStore)) } From ec8787d945f6f088c21b68234f4e3f3316d5d06f Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Thu, 2 Jun 2016 13:33:08 -0700 Subject: [PATCH 16/23] 1.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9d2ab9b..6efa532 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.4.0", + "version": "1.5.0", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From b937486edb3735710d771758a1ea767b5a2064b6 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Tue, 20 Sep 2016 14:44:48 -0700 Subject: [PATCH 17/23] WIP --- src/index.js | 66 ++++++++++++++++++++++----------------------------- test/index.js | 64 ++++++++++++++++++++++--------------------------- 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/index.js b/src/index.js index eeb9bf8..a4c8cd0 100644 --- a/src/index.js +++ b/src/index.js @@ -31,35 +31,47 @@ module.exports = (options) => { } } - if (options.localStorageKey) { - try { - token = ls.get(options.localStorageKey) - } catch (e) { - logger.warn('[JWT store] Unable to get token', e) + const refreshInterval = options.refreshInterval + ? options.refreshInterval + : (60000) + + const refreshToken = () => { + if (!token && options.refresh) { + tokenStore.refreshToken() + } else { + user = decodeToken(token) + } + + let expDate = user ? new Date(user.exp * 1000 - 2 * refreshInterval) : null + if (expDate && expDate < new Date() && options.refresh) { + tokenStore.refreshToken() } - } else { - token = cookie.get && cookie.get(options.cookie) } - logger.info('[JWT store] created store with token', token) const tokenStore = extend({ - getToken () { - return token - }, + init () { + let token + if (options.localStorageKey) { + try { + token = ls.get(options.localStorageKey) + } catch (e) { + logger.warn('[JWT store] Unable to get token', e) + } + } else { + token = cookie.get && cookie.get(options.cookie) + } - getUser () { - return user - }, + if (token) { this.setToken(token) } + refreshToken() - getUserId () { - return user ? user.id : void 0 + setInterval(refreshToken, refreshInterval) }, setToken (newToken) { logger.info('[JWT store] setting new token', newToken) token = newToken user = decodeToken(token) - this.emit('Token received') + this.emit('Token received', token, user) }, refreshToken () { @@ -69,25 +81,5 @@ module.exports = (options) => { } }, EventEmitter.prototype) - const refreshToken = () => { - if (!token && options.refresh) { - tokenStore.refreshToken() - } else { - user = decodeToken(token) - } - - let expDate = user ? new Date(user.exp * 1000 - 2 * refreshInterval) : null - if (expDate && expDate < new Date() && options.refresh) { - tokenStore.refreshToken() - } - } - - const refreshInterval = options.refreshInterval - ? options.refreshInterval - : (60000) - refreshToken() - - setInterval(refreshToken, refreshInterval) - return tokenStore } diff --git a/test/index.js b/test/index.js index c5b8882..df90ab5 100644 --- a/test/index.js +++ b/test/index.js @@ -41,41 +41,42 @@ describe('Token Store', () => { it('should set user after no token is present', () => { const tokenStore = require('../src')() - tokenStore.setToken(token) - let user = tokenStore.getUser() + tokenStore.on('Token received', (_, user) => { + assert.equal(user.first_name, 'Mike') + assert.equal(user.last_name, 'Atkins') + }) - assert.equal(user.first_name, 'Mike') - assert.equal(user.last_name, 'Atkins') + tokenStore.init() + tokenStore.setToken(token) }) it('should get the token out of local storage', () => { ls.set(localStorageKey, token) const tokenStore = require('../src')({localStorageKey}) - const user = tokenStore.getUser() - - assert.equal(user.first_name, 'Mike') - assert.equal(user.last_name, 'Atkins') + tokenStore.on('Token received', (_, user) => { + assert.equal(user.first_name, 'Mike') + assert.equal(user.last_name, 'Atkins') + }) + tokenStore.init() }) it('should catch an exception token is not present in local storage', () => { ls.set(localStorageKey, undefined) const tokenStore = require('../src')({localStorageKey}) - const token = tokenStore.getToken() - - assert.equal(token, undefined) + tokenStore.on('Token received', assert.fail) + tokenStore.init() }) it('if no token call refresh & set token', done => { const tokenStore = require('../src')({refresh: () => bluebird.resolve(updatedToken) }) - tokenStore.on('Token received', () => { - const user = tokenStore.getUser() - + tokenStore.on('Token received', (_, user) => { assert.equal(user.first_name, 'Mike') assert.equal(user.last_name, 'Atkins') done() }) + tokenStore.init() }) it('if token is expired, call refresh with expired token', done => { @@ -87,7 +88,7 @@ describe('Token Store', () => { done() return bluebird.resolve(updatedToken) } - }) + }).init() }) it('if token is expired, call refresh & set token', done => { @@ -97,13 +98,12 @@ describe('Token Store', () => { refresh: () => bluebird.resolve(updatedToken) }) - tokenStore.on('Token received', () => { - const user = tokenStore.getUser() - + tokenStore.on('Token received', (_, user) => { assert.equal(user.first_name, 'Mike') assert.equal(user.last_name, 'Atkins') done() }) + tokenStore.init() }) it('if token valid, leave as is', () => { @@ -112,11 +112,11 @@ describe('Token Store', () => { localStorageKey, refresh: () => assert.fail('should not be called') }) - - const user = tokenStore.getUser() - - assert.equal(user.first_name, 'Mike') - assert.equal(user.last_name, 'Atkins') + tokenStore.on('Token received', (_, user) => { + assert.equal(user.first_name, 'Mike') + assert.equal(user.last_name, 'Atkins') + }) + tokenStore.init() }) it('if token to expire soon, refresh after interval', done => { @@ -126,13 +126,12 @@ describe('Token Store', () => { refresh: () => bluebird.resolve(updatedToken), refreshInterval: 1000 }) - tokenStore.on('Token received', () => { - const user = tokenStore.getUser() - + tokenStore.on('Token received', (_, user) => { assert.equal(user.first_name, 'Mike') assert.equal(user.last_name, 'Atkins') done() }) + tokenStore.init() }) it('refreshes the token and sets it', done => { @@ -144,13 +143,12 @@ describe('Token Store', () => { assert.equal(tokenStore.getUser().timezone, undefined) - tokenStore.on('Token received', () => { - const user = tokenStore.getUser() - + tokenStore.on('Token received', (_, user) => { assert.equal(user.timezone, 'UTC') done() }) + tokenStore.init() tokenStore.refreshToken() }) @@ -158,9 +156,7 @@ describe('Token Store', () => { it('should not blow up when cookie is not present', () => { let tokenStore assert.doesNotThrow(() => tokenStore = require('../src')()) - assert.ok(tokenStore.getToken() === void 0) - assert.ok(tokenStore.getUser() === void 0) - assert.ok(tokenStore.getUserId() === void 0) + assert.doesNotThrow(() => tokenStore.init()) }) it('should not blow up when cookie is invalid', () => { @@ -169,9 +165,7 @@ describe('Token Store', () => { let tokenStore assert.doesNotThrow(() => tokenStore = require('../src')()) - assert.ok(tokenStore.getToken() === token) - assert.ok(tokenStore.getUser() === void 0) - assert.ok(tokenStore.getUserId() === void 0) + assert.doesNotThrow(() => tokenStore.init()) }) }) From 5e48325fd3ff8f11a87a5c30b78293aca284507b Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Fri, 23 Sep 2016 16:34:28 -0700 Subject: [PATCH 18/23] Doc n' tests --- README.md | 36 ++++++++++++------------------ test/index.js | 62 ++++++++++++++++++++++++++++----------------------- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index cf84206..5f3820d 100644 --- a/README.md +++ b/README.md @@ -5,28 +5,20 @@ React JWT user store ## Usage ```javascript -let React = require('react'), - userStore = require('react-jwt-store')(); - -class someComponent extends React.Component { - constructor() { - super(props); - - this.state = { - user: userStore.getUser(), - token: userStore.getToken() - }; - } - render() { - let user = this.state.user, - token = this.state.token; - - return ( -
-

{user}

-

{token}

- } -} +const userStore = require('react-jwt-store')() + +userStore.on('Token received', (token, user) => { + console.log(token, user) +}) + +userStore.init() +``` + +### Initialize +In order to trigger the store's refresh mechanism and send data to any event +handlers, you must call the `init` method. +```javascript +userStore.init() ``` ### Set the token diff --git a/test/index.js b/test/index.js index df90ab5..077c236 100644 --- a/test/index.js +++ b/test/index.js @@ -22,10 +22,10 @@ describe('Token Store', () => { beforeEach(() => { // HACK around https://github.com/auth0/jwt-decode/issues/5 - GLOBAL.window = GLOBAL + global.window = global // HACK around cookie monster returning undefined when document isn't there - GLOBAL.document = {} + global.document = {} }) beforeEach(() => { @@ -33,8 +33,8 @@ describe('Token Store', () => { }) afterEach(() => { - delete GLOBAL.window - delete GLOBAL.document + delete global.window + delete global.document ls.remove(localStorageKey) }) @@ -98,10 +98,11 @@ describe('Token Store', () => { refresh: () => bluebird.resolve(updatedToken) }) + let callCount = 0 tokenStore.on('Token received', (_, user) => { assert.equal(user.first_name, 'Mike') assert.equal(user.last_name, 'Atkins') - done() + if (++callCount === 2) { done() } }) tokenStore.init() }) @@ -126,10 +127,11 @@ describe('Token Store', () => { refresh: () => bluebird.resolve(updatedToken), refreshInterval: 1000 }) + let callCount = 0 tokenStore.on('Token received', (_, user) => { assert.equal(user.first_name, 'Mike') assert.equal(user.last_name, 'Atkins') - done() + if (++callCount === 2) { done() } }) tokenStore.init() }) @@ -141,11 +143,17 @@ describe('Token Store', () => { refresh: () => bluebird.resolve(tokenTimezone) }) - assert.equal(tokenStore.getUser().timezone, undefined) - + let callCount = 0 tokenStore.on('Token received', (_, user) => { - assert.equal(user.timezone, 'UTC') - done() + callCount++ + if (callCount === 1) { + assert(!user.timezone) + } else if (callCount === 2) { + assert.equal(user.timezone, 'UTC') + done() + } else { + assert.fail('shouldn\'t be called more than twice') + } }) tokenStore.init() @@ -170,47 +178,45 @@ describe('Token Store', () => { }) describe('default cookie key', () => { - let tokenStore + let tokenFromStore + let user beforeEach(() => { require('cookie-monster').set('XSRF-TOKEN', token) - tokenStore = require('../src')() + const tokenStore = require('../src')() + tokenStore.on('Token received', (t, u) => { + tokenFromStore = t + user = u + }) + tokenStore.init() }) it('should get the XSRF-TOKEN and return it', () => { - assert.equal(tokenStore.getToken(), token) + assert.equal(tokenFromStore, token) }) it('should return user', () => { - let user = tokenStore.getUser() - assert.equal(user.first_name, 'Mike') assert.equal(user.last_name, 'Atkins') }) - - it('should return user id', () => { - let id = tokenStore.getUserId() - assert.equal(id, 2751055) - }) - - it('should return token', () => { - let t = tokenStore.getToken() - assert.equal(token, t) - }) }) describe('override cookie key', () => { - let tokenStore + let tokenFromStore beforeEach(() => { require('cookie-monster').set('NOT-XSRF-TOKEN', token) - tokenStore = require('../src')({ cookie: 'NOT-XSRF-TOKEN' }) + const tokenStore = require('../src')({ cookie: 'NOT-XSRF-TOKEN' }) + tokenStore.on('Token received', (t) => { + tokenFromStore = t + }) + tokenStore.init() }) it('should get the NOT-XSRF-TOKEN and return it', () => { - assert.equal(tokenStore.getToken(), token) + assert.equal(tokenFromStore, token) }) }) }) From 220ff7b6d7404b9cf97cb8360712785ffa8338b5 Mon Sep 17 00:00:00 2001 From: Mike Atkins Date: Sun, 25 Sep 2016 13:26:37 -0700 Subject: [PATCH 19/23] 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6efa532..cc5d317 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-jwt-store", - "version": "1.5.0", + "version": "2.0.0", "description": "React JWT store", "license": "MIT", "author": "Seth Carney", From e20fc7c70ed747f99b1795a6cc66e30027667d94 Mon Sep 17 00:00:00 2001 From: Piotr Kowalski Date: Fri, 11 Aug 2017 02:23:16 +0100 Subject: [PATCH 20/23] Be able to clear refresh interval --- src/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index a4c8cd0..7f54a0b 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ const noop = function () { } module.exports = (options) => { let user let token + let refreshTimer options = extend({ cookie: 'XSRF-TOKEN' }, options) @@ -64,7 +65,7 @@ module.exports = (options) => { if (token) { this.setToken(token) } refreshToken() - setInterval(refreshToken, refreshInterval) + refreshTimer = setInterval(refreshToken, refreshInterval) }, setToken (newToken) { @@ -78,6 +79,12 @@ module.exports = (options) => { logger.info('[JWT store] refreshing token', token) options.refresh(token) .then(tokenStore.setToken.bind(tokenStore)) + }, + + terminate () { + user = undefined + token = undefined + clearInterval(refreshTimer) } }, EventEmitter.prototype) From a790fd90e8ed9579ae8303c1729c38d22c43b286 Mon Sep 17 00:00:00 2001 From: Piotr Kowalski Date: Fri, 11 Aug 2017 10:29:56 +0100 Subject: [PATCH 21/23] Fix ReferenceError: [BABEL] src/index.js: Using removed Babel 5 option --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index cc5d317..869a9ba 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,6 @@ "local-storage": "^1.4.2", "xtend": "^4.0.0" }, - "babel": { - "stage": 0 - }, "standard": { "parser": "babel-eslint", "globals": [ From fcd3b91739dafc0c7c5c0ab4d2245736d3fc34fe Mon Sep 17 00:00:00 2001 From: Piotr Kowalski Date: Sat, 19 Aug 2017 02:11:24 +0100 Subject: [PATCH 22/23] Test explicit termination - it should set token to undefined --- test/index.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/index.js b/test/index.js index 077c236..d58940a 100644 --- a/test/index.js +++ b/test/index.js @@ -219,4 +219,57 @@ describe('Token Store', () => { assert.equal(tokenFromStore, token) }) }) + + describe('terminate', () => { + let cookieMonster + beforeEach(() => { + cookieMonster = require('cookie-monster') + cookieMonster.set('XSRF-TOKEN', token) + }) + it('should set token to undefined on explicit termination', done => { + let callCount = 0 + const tokenStore = require('../src')({ + refresh: (t) => { + if (callCount === 0) { + cookieMonster.set('XSRF-TOKEN', token, {expires: 'Thu, 01 Jan 1970 00:00:01 GMT'}) + assert.equal(t, token) + } + if (callCount === 1) { + assert.equal(t, undefined) + } + callCount++ + + return bluebird.resolve(t) + } + }) + tokenStore.init() + tokenStore.terminate() + tokenStore.refreshToken() + done() + }) + + it('should not set token to undefined when no explicit termination', done => { + let callCount = 0 + const tokenStore = require('../src')({ + refresh: (t) => { + if (!t) { + return bluebird.resolve() + } + if (callCount === 0) { + cookieMonster.set('XSRF-TOKEN', token, {expires: 'Thu, 01 Jan 1970 00:00:01 GMT'}) + assert.equal(t, token) + } + if (callCount === 1) { + assert.equal(t, token) + } + callCount++ + + return bluebird.resolve(t) + } + }) + tokenStore.init() + tokenStore.refreshToken() + done() + }) + }) }) From 14f39ca5c25266b7a956e546cf672b1d72676a66 Mon Sep 17 00:00:00 2001 From: Piotr Kowalski Date: Mon, 30 Oct 2017 09:18:24 +0000 Subject: [PATCH 23/23] Add documentation for `terminate` feature --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 5f3820d..40ead81 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,15 @@ by passing `cookie` on the `options` hash: By default, the store does not log anything, but if you pass in a `console` compatible logger, the store will log the state of the token as it changes. + +### Terminate + +By default, if you set token to null: +```javascript +userStore.setToken(null) +``` +...an interval responsible for refreshing tokens (the one set up on init) will not be cleared. +If you want to clear it explicitly, then you can do it with: +```javascript +userStore.terminate() +```