From a20b7ec3d42ce1f53ce8ccfb4068a1ee9ef02dbe Mon Sep 17 00:00:00 2001 From: SleepWalker Date: Thu, 11 Aug 2016 22:20:14 +0300 Subject: [PATCH] #188: persist oauth data if if user tries to register during oauth --- src/components/auth/actions.js | 6 +++ src/services/authFlow/AuthFlow.js | 22 ++++++--- tests/services/authFlow/AuthFlow.test.js | 60 +++++++++++++++++++++--- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/components/auth/actions.js b/src/components/auth/actions.js index 2533458..761c326 100644 --- a/src/components/auth/actions.js +++ b/src/components/auth/actions.js @@ -142,12 +142,18 @@ export function oAuthValidate(oauthData) { dispatch(setClient(resp.client)); dispatch(setOAuthRequest(resp.oAuth)); dispatch(setScopes(resp.session.scopes)); + localStorage.setItem('oauthData', JSON.stringify({ // @see services/authFlow/AuthFlow + timestamp: Date.now(), + payload: oauthData + })); }) .catch(handleOauthParamsValidation) ); } export function oAuthComplete(params = {}) { + localStorage.removeItem('oauthData'); + return wrapInLoader((dispatch, getState) => oauth.complete(getState().auth.oauth, params) .then((resp) => { diff --git a/src/services/authFlow/AuthFlow.js b/src/services/authFlow/AuthFlow.js index 1db8404..d33d925 100644 --- a/src/services/authFlow/AuthFlow.js +++ b/src/services/authFlow/AuthFlow.js @@ -8,8 +8,6 @@ import RecoverPasswordState from './RecoverPasswordState'; import ActivationState from './ActivationState'; import ResendActivationState from './ResendActivationState'; -// TODO: a way to unload service (when we are on account page) - export default class AuthFlow { constructor(actions) { if (typeof actions !== 'object') { @@ -41,6 +39,8 @@ export default class AuthFlow { this.getState = store.getState.bind(store); this.dispatch = store.dispatch.bind(store); + + this.restoreOAuthState(); } resolve(payload = {}) { @@ -131,11 +131,6 @@ export default class AuthFlow { this.currentRequest = request; - if (path === '/') { - // reset oauth data if user tried to navigate to index route - this.run('setOAuthRequest', {}); - } - switch (path) { case '/register': this.setState(new RegisterState()); @@ -179,4 +174,17 @@ export default class AuthFlow { this.onReady(); } + + /** + * @api private + */ + restoreOAuthState() { + try { + const data = JSON.parse(localStorage.getItem('oauthData')); + + if (Date.now() - data.timestamp < 60 * 60 * 1000) { + this.run('oAuthValidate', data.payload); + } + } catch (err) {/* bad luck :( */} + } } diff --git a/tests/services/authFlow/AuthFlow.test.js b/tests/services/authFlow/AuthFlow.test.js index 15d80c6..5ea9661 100644 --- a/tests/services/authFlow/AuthFlow.test.js +++ b/tests/services/authFlow/AuthFlow.test.js @@ -33,6 +33,59 @@ describe('AuthFlow', () => { expect(() => flow.actions.test = 'hacked', 'to throw', /readonly/); }); + describe('#setStore', () => { + afterEach(() => { + localStorage.removeItem('oauthData'); + }); + + it('should create #navigate, #getState, #dispatch', () => { + flow.setStore({ + getState() {}, + dispatch() {} + }); + + expect(flow.getState, 'to be defined'); + expect(flow.dispatch, 'to be defined'); + expect(flow.navigate, 'to be defined'); + }); + + it('should restore oauth state from localStorage', () => { + const oauthData = {}; + localStorage.setItem('oauthData', JSON.stringify({ + timestamp: Date.now() - 10, + payload: oauthData + })); + + sinon.stub(flow, 'run').named('flow.run'); + + flow.setStore({ + getState() {}, + dispatch() {} + }); + + expect(flow.run, 'to have a call satisfying', [ + 'oAuthValidate', oauthData + ]); + }); + + it('should not restore outdated (>1h) oauth state', () => { + const oauthData = {}; + localStorage.setItem('oauthData', JSON.stringify({ + timestamp: Date.now() - 60 * 60 * 1000, + payload: oauthData + })); + + sinon.stub(flow, 'run').named('flow.run'); + + flow.setStore({ + getState() {}, + dispatch() {} + }); + + expect(flow.run, 'was not called'); + }); + }); + describe('#setState', () => { it('should change state', () => { const state = new AbstractState(); @@ -198,13 +251,6 @@ describe('AuthFlow', () => { }); }); - it('should run setOAuthRequest if /', () => { - flow.handleRequest({path: '/'}); - - expect(flow.run, 'was called once'); - expect(flow.run, 'to have a call satisfying', ['setOAuthRequest', {}]); - }); - it('should call callback', () => { const callback = sinon.stub().named('callback');