2016-05-01 16:01:40 +05:30
|
|
|
import { routeActions } from 'react-router-redux';
|
|
|
|
|
2016-02-26 11:55:47 +05:30
|
|
|
import request from 'services/request';
|
2016-07-31 19:23:16 +05:30
|
|
|
import captcha from 'services/captcha';
|
2016-05-01 15:58:54 +05:30
|
|
|
import accounts from 'services/api/accounts';
|
2016-06-28 15:43:27 +05:30
|
|
|
import authentication from 'services/api/authentication';
|
2016-05-20 01:11:43 +05:30
|
|
|
import { setLocale } from 'components/i18n/actions';
|
2016-02-26 11:55:47 +05:30
|
|
|
|
2016-02-13 20:58:47 +05:30
|
|
|
export const UPDATE = 'USER_UPDATE';
|
|
|
|
/**
|
2016-07-29 01:21:47 +05:30
|
|
|
* @param {string|object} payload jwt token or user object
|
|
|
|
* @return {object} action definition
|
2016-02-13 20:58:47 +05:30
|
|
|
*/
|
|
|
|
export function updateUser(payload) {
|
|
|
|
return {
|
|
|
|
type: UPDATE,
|
|
|
|
payload
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-05-20 01:11:43 +05:30
|
|
|
export const CHANGE_LANG = 'USER_CHANGE_LANG';
|
|
|
|
export function changeLang(lang) {
|
|
|
|
return (dispatch, getState) => dispatch(setLocale(lang))
|
|
|
|
.then((lang) => {
|
|
|
|
const {user: {isGuest, lang: oldLang}} = getState();
|
|
|
|
|
|
|
|
if (!isGuest && oldLang !== lang) {
|
|
|
|
accounts.changeLang(lang);
|
|
|
|
}
|
|
|
|
|
2016-07-31 19:23:16 +05:30
|
|
|
// TODO: probably should be moved from here, because it is side effect
|
|
|
|
captcha.setLang(lang);
|
|
|
|
|
2016-05-20 01:11:43 +05:30
|
|
|
dispatch({
|
|
|
|
type: CHANGE_LANG,
|
|
|
|
payload: {
|
|
|
|
lang
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-02-13 20:58:47 +05:30
|
|
|
export const SET = 'USER_SET';
|
|
|
|
export function setUser(payload) {
|
|
|
|
return {
|
|
|
|
type: SET,
|
|
|
|
payload
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function logout() {
|
2016-07-29 01:21:47 +05:30
|
|
|
return (dispatch, getState) =>
|
|
|
|
authentication.logout().then(() => {
|
|
|
|
dispatch(setUser({
|
|
|
|
lang: getState().user.lang,
|
|
|
|
isGuest: true
|
|
|
|
}));
|
|
|
|
dispatch(routeActions.push('/login'));
|
|
|
|
});
|
2016-02-13 20:58:47 +05:30
|
|
|
}
|
2016-02-26 11:55:47 +05:30
|
|
|
|
|
|
|
export function fetchUserData() {
|
2016-06-04 22:24:42 +05:30
|
|
|
return (dispatch) =>
|
2016-05-01 15:58:54 +05:30
|
|
|
accounts.current()
|
2016-06-04 20:28:29 +05:30
|
|
|
.then((resp) => {
|
|
|
|
dispatch(updateUser(resp));
|
2016-05-20 01:11:43 +05:30
|
|
|
|
2016-06-04 20:28:29 +05:30
|
|
|
return dispatch(changeLang(resp.lang));
|
|
|
|
});
|
2016-02-26 11:55:47 +05:30
|
|
|
}
|
2016-02-26 23:43:41 +05:30
|
|
|
|
2016-08-03 00:29:29 +05:30
|
|
|
export function acceptRules() {
|
|
|
|
return (dispatch) =>
|
|
|
|
accounts.acceptRules()
|
|
|
|
.then((resp) => {
|
|
|
|
dispatch(updateUser({
|
|
|
|
shouldAcceptRules: false
|
|
|
|
}));
|
|
|
|
|
|
|
|
return resp;
|
|
|
|
})
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2016-06-04 22:24:42 +05:30
|
|
|
let middlewareAdded = false;
|
2016-06-04 20:28:29 +05:30
|
|
|
export function authenticate(token, refreshToken) { // TODO: this action, probably, belongs to components/auth
|
|
|
|
return (dispatch, getState) => {
|
2016-06-04 22:24:42 +05:30
|
|
|
if (!middlewareAdded) {
|
|
|
|
request.addMiddleware(tokenCheckMiddleware(dispatch, getState));
|
|
|
|
request.addMiddleware(tokenApplyMiddleware(dispatch, getState));
|
|
|
|
middlewareAdded = true;
|
2016-06-04 20:28:29 +05:30
|
|
|
}
|
2016-06-04 19:16:39 +05:30
|
|
|
|
2016-06-04 22:24:42 +05:30
|
|
|
refreshToken = refreshToken || getState().user.refreshToken;
|
|
|
|
dispatch(updateUser({
|
|
|
|
token,
|
|
|
|
refreshToken
|
|
|
|
}));
|
|
|
|
|
2016-06-04 19:16:39 +05:30
|
|
|
return dispatch(fetchUserData()).then((resp) => {
|
|
|
|
dispatch(updateUser({
|
2016-06-04 22:24:42 +05:30
|
|
|
isGuest: false
|
2016-06-04 19:16:39 +05:30
|
|
|
}));
|
|
|
|
return resp;
|
|
|
|
});
|
2016-02-26 23:43:41 +05:30
|
|
|
};
|
|
|
|
}
|
2016-06-04 20:28:29 +05:30
|
|
|
|
2016-06-04 22:24:42 +05:30
|
|
|
function requestAccessToken(refreshToken, dispatch) {
|
|
|
|
let promise;
|
|
|
|
if (refreshToken) {
|
|
|
|
promise = authentication.refreshToken(refreshToken);
|
|
|
|
} else {
|
|
|
|
promise = Promise.reject();
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise
|
|
|
|
.then((resp) => dispatch(updateUser({
|
|
|
|
token: resp.access_token
|
|
|
|
})))
|
|
|
|
.catch(() => dispatch(logout()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensures, that all user's requests have fresh access token
|
|
|
|
*
|
2016-07-29 01:21:47 +05:30
|
|
|
* @param {function} dispatch
|
|
|
|
* @param {function} getState
|
2016-06-04 22:24:42 +05:30
|
|
|
*
|
2016-07-29 01:21:47 +05:30
|
|
|
* @return {object} middleware
|
2016-06-04 22:24:42 +05:30
|
|
|
*/
|
|
|
|
function tokenCheckMiddleware(dispatch, getState) {
|
|
|
|
return {
|
|
|
|
before(data) {
|
|
|
|
const {isGuest, refreshToken, token} = getState().user;
|
|
|
|
const isRefreshTokenRequest = data.url.includes('refresh-token');
|
|
|
|
|
|
|
|
if (isGuest || isRefreshTokenRequest || !token) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
const SAFETY_FACTOR = 60; // ask new token earlier to overcome time dissynchronization problem
|
|
|
|
const jwt = getJWTPayload(token);
|
|
|
|
|
|
|
|
if (jwt.exp - SAFETY_FACTOR < Date.now() / 1000) {
|
|
|
|
return requestAccessToken(refreshToken, dispatch).then(() => data);
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
|
|
|
|
catch(resp, restart) {
|
|
|
|
/*
|
|
|
|
{
|
|
|
|
"name": "Unauthorized",
|
|
|
|
"message": "You are requesting with an invalid credential.",
|
|
|
|
"code": 0,
|
|
|
|
"status": 401,
|
|
|
|
"type": "yii\\web\\UnauthorizedHttpException"
|
|
|
|
}
|
|
|
|
{
|
|
|
|
"name": "Unauthorized",
|
|
|
|
"message": "Token expired",
|
|
|
|
"code": 0,
|
|
|
|
"status": 401,
|
|
|
|
"type": "yii\\web\\UnauthorizedHttpException"
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
if (resp && resp.status === 401) {
|
|
|
|
const {refreshToken} = getState().user;
|
|
|
|
if (resp.message === 'Token expired' && refreshToken) {
|
|
|
|
// request token and retry
|
|
|
|
return requestAccessToken(refreshToken, dispatch).then(restart);
|
|
|
|
}
|
|
|
|
|
|
|
|
dispatch(logout());
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.reject(resp);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Applies Bearer header for all requests
|
|
|
|
*
|
2016-07-29 01:21:47 +05:30
|
|
|
* @param {function} dispatch
|
|
|
|
* @param {function} getState
|
2016-06-04 22:24:42 +05:30
|
|
|
*
|
2016-07-29 01:21:47 +05:30
|
|
|
* @return {object} middleware
|
2016-06-04 22:24:42 +05:30
|
|
|
*/
|
|
|
|
function tokenApplyMiddleware(dispatch, getState) {
|
|
|
|
return {
|
|
|
|
before(data) {
|
|
|
|
const {token} = getState().user;
|
|
|
|
|
|
|
|
if (token) {
|
|
|
|
data.options.headers.Authorization = `Bearer ${token}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-06-04 20:28:29 +05:30
|
|
|
function getJWTPayload(jwt) {
|
|
|
|
const parts = (jwt || '').split('.');
|
|
|
|
|
|
|
|
if (parts.length !== 3) {
|
|
|
|
throw new Error('Invalid jwt token');
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
return JSON.parse(atob(parts[1]));
|
|
|
|
} catch (err) {
|
|
|
|
throw new Error('Can not decode jwt token');
|
|
|
|
}
|
|
|
|
}
|