#126: slightly refactored login and jwt token logic

This commit is contained in:
SleepWalker 2016-06-04 16:46:39 +03:00
parent d0bfa9fdb8
commit 818101b710
7 changed files with 52 additions and 41 deletions

View File

@ -2,6 +2,7 @@ import { routeActions } from 'react-router-redux';
import { updateUser, logout as logoutUser, changePassword as changeUserPassword, authenticate } from 'components/user/actions'; import { updateUser, logout as logoutUser, changePassword as changeUserPassword, authenticate } from 'components/user/actions';
import request from 'services/request'; import request from 'services/request';
import authentication from 'services/api/authentication';
export function login({login = '', password = '', rememberMe = false}) { export function login({login = '', password = '', rememberMe = false}) {
const PASSWORD_REQUIRED = 'error.password_required'; const PASSWORD_REQUIRED = 'error.password_required';
@ -9,28 +10,21 @@ export function login({login = '', password = '', rememberMe = false}) {
const ACTIVATION_REQUIRED = 'error.account_not_activated'; const ACTIVATION_REQUIRED = 'error.account_not_activated';
return wrapInLoader((dispatch) => return wrapInLoader((dispatch) =>
request.post( authentication.login(
'/api/authentication/login',
{login, password, rememberMe} {login, password, rememberMe}
) )
.then((resp) => { .then(authHandler(dispatch))
dispatch(updateUser({
isGuest: false,
token: resp.jwt
}));
return dispatch(authenticate(resp.jwt));
})
.catch((resp) => { .catch((resp) => {
if (resp.errors.login === ACTIVATION_REQUIRED) { if (resp.errors) {
return dispatch(needActivation()); if (resp.errors.password === PASSWORD_REQUIRED) {
} else if (resp.errors.password === PASSWORD_REQUIRED) { return dispatch(updateUser({
return dispatch(updateUser({ username: login,
username: login, email: login
email: login }));
})); } else if (resp.errors.login === ACTIVATION_REQUIRED) {
} else if (resp.errors) { return dispatch(needActivation());
if (resp.errors.login === LOGIN_REQUIRED && password) { } else if (resp.errors.login === LOGIN_REQUIRED && password) {
// return to the first step
dispatch(logout()); dispatch(logout());
} }
} }
@ -76,14 +70,7 @@ export function recoverPassword({
'/api/authentication/recover-password', '/api/authentication/recover-password',
{key, newPassword, newRePassword} {key, newPassword, newRePassword}
) )
.then((resp) => { .then(authHandler(dispatch))
dispatch(updateUser({
isGuest: false,
isActive: true
}));
return dispatch(authenticate(resp.jwt));
})
.catch(validationErrorsHandler(dispatch, '/forgot-password')) .catch(validationErrorsHandler(dispatch, '/forgot-password'))
); );
} }
@ -118,14 +105,7 @@ export function activate({key = ''}) {
'/api/signup/confirm', '/api/signup/confirm',
{key} {key}
) )
.then((resp) => { .then(authHandler(dispatch))
dispatch(updateUser({
isGuest: false,
isActive: true
}));
return dispatch(authenticate(resp.jwt));
})
.catch(validationErrorsHandler(dispatch, '/resend-activation')) .catch(validationErrorsHandler(dispatch, '/resend-activation'))
); );
} }
@ -341,6 +321,10 @@ function needActivation() {
}); });
} }
function authHandler(dispatch) {
return (resp) => dispatch(authenticate(resp.access_token, resp.refresh_token));
}
function validationErrorsHandler(dispatch, repeatUrl) { function validationErrorsHandler(dispatch, repeatUrl) {
return (resp) => { return (resp) => {
if (resp.errors) { if (resp.errors) {

View File

@ -32,6 +32,7 @@ export default class PasswordBody extends BaseAuthBody {
{user.email || user.username} {user.email || user.username}
</div> </div>
</div> </div>
<Input {...this.bindField('password')} <Input {...this.bindField('password')}
icon="key" icon="key"
type="password" type="password"
@ -40,7 +41,7 @@ export default class PasswordBody extends BaseAuthBody {
/> />
<Checkbox {...this.bindField('rememberMe')} <Checkbox {...this.bindField('rememberMe')}
defaultChecked={true} defaultChecked
label={messages.rememberMe} label={messages.rememberMe}
/> />
</div> </div>

View File

@ -19,6 +19,7 @@ export default class User {
id: null, id: null,
uuid: null, uuid: null,
token: '', token: '',
refreshToken: '',
username: '', username: '',
email: '', email: '',
// will contain user's email or masked email // will contain user's email or masked email

View File

@ -95,13 +95,21 @@ export function changePassword({
} }
export function authenticate(token) { export function authenticate(token, refreshToken) {
if (!token || token.split('.').length !== 3) { if (!token || token.split('.').length !== 3) {
throw new Error('Invalid token'); throw new Error('Invalid token');
} }
return (dispatch) => { return (dispatch) => {
request.setAuthToken(token); request.setAuthToken(token);
return dispatch(fetchUserData());
return dispatch(fetchUserData()).then((resp) => {
dispatch(updateUser({
isGuest: false,
token,
refreshToken
}));
return resp;
});
}; };
} }

View File

@ -0,0 +1,14 @@
import request from 'services/request';
export default {
login({
login = '',
password = '',
rememberMe = false
}) {
return request.post(
'/api/authentication/login',
{login, password, rememberMe}
);
}
};

View File

@ -14,11 +14,12 @@ export default class PasswordState extends AbstractState {
} }
} }
resolve(context, {password}) { resolve(context, {password, rememberMe}) {
const {user} = context.getState(); const {user} = context.getState();
context.run('login', { context.run('login', {
password, password,
rememberMe,
login: user.email || user.username login: user.email || user.username
}) })
.then(() => context.setState(new CompleteState())); .then(() => context.setState(new CompleteState()));

View File

@ -48,6 +48,7 @@ describe('PasswordState', () => {
(function() { (function() {
const expectedLogin = 'login'; const expectedLogin = 'login';
const expectedPassword = 'password'; const expectedPassword = 'password';
const expectedRememberMe = true;
const testWith = (user) => { const testWith = (user) => {
it(`should call login with email or username and password. User: ${JSON.stringify(user)}`, () => { it(`should call login with email or username and password. User: ${JSON.stringify(user)}`, () => {
@ -58,11 +59,12 @@ describe('PasswordState', () => {
'login', 'login',
sinon.match({ sinon.match({
login: expectedLogin, login: expectedLogin,
password: expectedPassword password: expectedPassword,
rememberMe: expectedRememberMe,
}) })
).returns({then() {}}); ).returns({then() {}});
state.resolve(context, {password: expectedPassword}); state.resolve(context, {password: expectedPassword, rememberMe: expectedRememberMe});
}); });
}; };