#245: add register link to account chooser auth panel

This commit is contained in:
SleepWalker
2017-01-31 08:05:36 +02:00
parent b2202f8831
commit a685813934
11 changed files with 77 additions and 43 deletions

View File

@@ -3,6 +3,7 @@ import { routeActions } from 'react-router-redux';
import authentication from 'services/api/authentication'; import authentication from 'services/api/authentication';
import { updateUser, setGuest } from 'components/user/actions'; import { updateUser, setGuest } from 'components/user/actions';
import { setLocale } from 'components/i18n/actions'; import { setLocale } from 'components/i18n/actions';
import { setAccountSwitcher } from 'components/auth/actions';
import logger from 'services/logger'; import logger from 'services/logger';
import { import {
@@ -32,7 +33,7 @@ export { updateToken };
* @return {function} * @return {function}
*/ */
export function authenticate({token, refreshToken}) { export function authenticate({token, refreshToken}) {
return (dispatch) => return (dispatch, getState) =>
authentication.validateToken({token, refreshToken}) authentication.validateToken({token, refreshToken})
.catch((resp) => { .catch((resp) => {
logger.warn('Error validating token during auth', { logger.warn('Error validating token during auth', {
@@ -56,6 +57,8 @@ export function authenticate({token, refreshToken}) {
} }
})) }))
.then(({user, account}) => { .then(({user, account}) => {
const {auth} = getState();
dispatch(add(account)); dispatch(add(account));
dispatch(activate(account)); dispatch(activate(account));
dispatch(updateUser(user)); dispatch(updateUser(user));
@@ -68,6 +71,14 @@ export function authenticate({token, refreshToken}) {
sessionStorage.setItem(`stranger${account.id}`, 1); sessionStorage.setItem(`stranger${account.id}`, 1);
} }
if (auth && auth.oauth && auth.oauth.clientId) {
// if we authenticating during oauth, we disable account chooser
// because user probably has made his choise now
// this may happen, when user registers, logs in or uses account
// chooser panel during oauth
dispatch(setAccountSwitcher(false));
}
return dispatch(setLocale(user.lang)) return dispatch(setLocale(user.lang))
.then(() => account); .then(() => account);
}); });

View File

@@ -2,5 +2,6 @@
"chooseAccountTitle": "Choose an account", "chooseAccountTitle": "Choose an account",
"addAccount": "Log into another account", "addAccount": "Log into another account",
"logoutAll": "Log out from all accounts", "logoutAll": "Log out from all accounts",
"createNewAccount": "Create new account",
"description": "You have logged in into multiple accounts. Please choose the one, you want to use to authorize {appName}" "description": "You have logged in into multiple accounts. Please choose the one, you want to use to authorize {appName}"
} }

View File

@@ -10,7 +10,11 @@ export default factory({
}, },
links: [ links: [
{ {
label: messages.logoutAll label: messages.createNewAccount
},
{
label: messages.logoutAll,
payload: {logout: true}
} }
] ]
}); });

View File

@@ -1,6 +1,7 @@
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import LoginState from './LoginState'; import LoginState from './LoginState';
import CompleteState from './CompleteState'; import CompleteState from './CompleteState';
import RegisterState from './RegisterState';
export default class ChooseAccountState extends AbstractState { export default class ChooseAccountState extends AbstractState {
enter(context) { enter(context) {
@@ -8,9 +9,6 @@ export default class ChooseAccountState extends AbstractState {
} }
resolve(context, payload) { resolve(context, payload) {
// do not ask again after user adds account, or chooses an existed one
context.run('setAccountSwitcher', false);
if (payload.id) { if (payload.id) {
context.setState(new CompleteState()); context.setState(new CompleteState());
} else { } else {
@@ -19,7 +17,16 @@ export default class ChooseAccountState extends AbstractState {
} }
} }
reject(context) { /**
context.run('logout'); * @param {object} context
* @param {object} payload
* @param {bool} [payload.logout=false]
*/
reject(context, payload = {}) {
if (payload.logout) {
context.run('logout');
} else {
context.setState(new RegisterState());
}
} }
} }

View File

@@ -24,11 +24,7 @@ export default class PasswordState extends AbstractState {
rememberMe, rememberMe,
login login
}) })
.then(() => { .then(() => context.setState(new CompleteState()))
context.run('setAccountSwitcher', false);
context.setState(new CompleteState());
})
.catch((err = {}) => err.errors || logger.warn(err)); .catch((err = {}) => err.errors || logger.warn(err));
} }

View File

@@ -7,13 +7,7 @@ import ResendActivationState from './ResendActivationState';
export default class RegisterState extends AbstractState { export default class RegisterState extends AbstractState {
enter(context) { enter(context) {
const {user} = context.getState(); context.navigate('/register');
if (user.isGuest) {
context.navigate('/register');
} else {
context.setState(new CompleteState());
}
} }
resolve(context, payload) { resolve(context, payload) {

View File

@@ -14,4 +14,4 @@ export default {
return document.referrer.includes(`${location.protocol}//${location.host}`) return document.referrer.includes(`${location.protocol}//${location.host}`)
|| this.initialLength < window.history.length; || this.initialLength < window.history.length;
} }
} };

View File

@@ -19,6 +19,7 @@ import {
import { SET_LOCALE } from 'components/i18n/actions'; import { SET_LOCALE } from 'components/i18n/actions';
import { updateUser, setUser } from 'components/user/actions'; import { updateUser, setUser } from 'components/user/actions';
import { setAccountSwitcher } from 'components/auth/actions';
const account = { const account = {
id: 1, id: 1,
@@ -67,7 +68,7 @@ describe('components/accounts/actions', () => {
describe('#authenticate()', () => { describe('#authenticate()', () => {
it('should request user state using token', () => it('should request user state using token', () =>
authenticate(account)(dispatch).then(() => authenticate(account)(dispatch, getState).then(() =>
expect(authentication.validateToken, 'to have a call satisfying', [ expect(authentication.validateToken, 'to have a call satisfying', [
{token: account.token, refreshToken: account.refreshToken} {token: account.token, refreshToken: account.refreshToken}
]) ])
@@ -75,7 +76,7 @@ describe('components/accounts/actions', () => {
); );
it(`dispatches ${ADD} action`, () => it(`dispatches ${ADD} action`, () =>
authenticate(account)(dispatch).then(() => authenticate(account)(dispatch, getState).then(() =>
expect(dispatch, 'to have a call satisfying', [ expect(dispatch, 'to have a call satisfying', [
add(account) add(account)
]) ])
@@ -83,7 +84,7 @@ describe('components/accounts/actions', () => {
); );
it(`dispatches ${ACTIVATE} action`, () => it(`dispatches ${ACTIVATE} action`, () =>
authenticate(account)(dispatch).then(() => authenticate(account)(dispatch, getState).then(() =>
expect(dispatch, 'to have a call satisfying', [ expect(dispatch, 'to have a call satisfying', [
activate(account) activate(account)
]) ])
@@ -91,7 +92,7 @@ describe('components/accounts/actions', () => {
); );
it(`dispatches ${SET_LOCALE} action`, () => it(`dispatches ${SET_LOCALE} action`, () =>
authenticate(account)(dispatch).then(() => authenticate(account)(dispatch, getState).then(() =>
expect(dispatch, 'to have a call satisfying', [ expect(dispatch, 'to have a call satisfying', [
{type: SET_LOCALE, payload: {locale: 'be'}} {type: SET_LOCALE, payload: {locale: 'be'}}
]) ])
@@ -99,7 +100,7 @@ describe('components/accounts/actions', () => {
); );
it('should update user state', () => it('should update user state', () =>
authenticate(account)(dispatch).then(() => authenticate(account)(dispatch, getState).then(() =>
expect(dispatch, 'to have a call satisfying', [ expect(dispatch, 'to have a call satisfying', [
updateUser({...user, isGuest: false}) updateUser({...user, isGuest: false})
]) ])
@@ -107,7 +108,7 @@ describe('components/accounts/actions', () => {
); );
it('resolves with account', () => it('resolves with account', () =>
authenticate(account)(dispatch).then((resp) => authenticate(account)(dispatch, getState).then((resp) =>
expect(resp, 'to equal', account) expect(resp, 'to equal', account)
) )
); );
@@ -115,7 +116,7 @@ describe('components/accounts/actions', () => {
it('rejects when bad auth data', () => { it('rejects when bad auth data', () => {
authentication.validateToken.returns(Promise.reject({})); authentication.validateToken.returns(Promise.reject({}));
return expect(authenticate(account)(dispatch), 'to be rejected').then(() => { return expect(authenticate(account)(dispatch, getState), 'to be rejected').then(() => {
expect(dispatch, 'to have a call satisfying', [ expect(dispatch, 'to have a call satisfying', [
{payload: {isGuest: true}}, {payload: {isGuest: true}},
]); ]);
@@ -134,11 +135,37 @@ describe('components/accounts/actions', () => {
sessionStorage.removeItem(expectedKey); sessionStorage.removeItem(expectedKey);
return authenticate(account)(dispatch).then(() => { return authenticate(account)(dispatch, getState).then(() => {
expect(sessionStorage.getItem(expectedKey), 'not to be null'); expect(sessionStorage.getItem(expectedKey), 'not to be null');
sessionStorage.removeItem(expectedKey); sessionStorage.removeItem(expectedKey);
}); });
}); });
describe('when user authenticated during oauth', () => {
beforeEach(() => {
getState.returns({
accounts: {
available: [],
active: null
},
user: {},
auth: {
oauth: {
clientId: 'ely.by',
prompt: []
}
}
});
});
it('should dispatch setAccountSwitcher', () =>
authenticate(account)(dispatch, getState).then(() =>
expect(dispatch, 'to have a call satisfying', [
setAccountSwitcher(false)
])
)
);
});
}); });
describe('#revoke()', () => { describe('#revoke()', () => {

View File

@@ -1,6 +1,7 @@
import ChooseAccountState from 'services/authFlow/ChooseAccountState'; import ChooseAccountState from 'services/authFlow/ChooseAccountState';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
import LoginState from 'services/authFlow/LoginState'; import LoginState from 'services/authFlow/LoginState';
import RegisterState from 'services/authFlow/RegisterState';
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers'; import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
@@ -31,14 +32,12 @@ describe('ChooseAccountState', () => {
describe('#resolve', () => { describe('#resolve', () => {
it('should transition to complete if existed account was choosen', () => { it('should transition to complete if existed account was choosen', () => {
expectRun(mock, 'setAccountSwitcher', false);
expectState(mock, CompleteState); expectState(mock, CompleteState);
state.resolve(context, {id: 123}); state.resolve(context, {id: 123});
}); });
it('should transition to login if user wants to add new account', () => { it('should transition to login if user wants to add new account', () => {
expectRun(mock, 'setAccountSwitcher', false);
expectNavigate(mock, '/login'); expectNavigate(mock, '/login');
expectState(mock, LoginState); expectState(mock, LoginState);
@@ -47,10 +46,16 @@ describe('ChooseAccountState', () => {
}); });
describe('#reject', () => { describe('#reject', () => {
it('should logout', () => { it('should transition to register', () => {
expectRun(mock, 'logout'); expectState(mock, RegisterState);
state.reject(context); state.reject(context);
}); });
it('should logout', () => {
expectRun(mock, 'logout');
state.reject(context, {logout: true});
});
}); });
}); });

View File

@@ -61,7 +61,6 @@ describe('PasswordState', () => {
} }
}); });
expectRun(mock, 'setAccountSwitcher', false);
expectRun( expectRun(
mock, mock,
'login', 'login',

View File

@@ -34,16 +34,6 @@ describe('RegisterState', () => {
state.enter(context); state.enter(context);
}); });
it('should transition to complete if not guest', () => {
context.getState.returns({
user: {isGuest: false}
});
expectState(mock, CompleteState);
state.enter(context);
});
}); });
describe('#resolve', () => { describe('#resolve', () => {