Deauthenticate user if refresh was not possible (session expired or total delta reached)
import * as types from '../constants/actionTypes';
import { all, call, fork, race, take, cancelled, put, select } from 'redux-saga/effects'
import config from '../config';
// Utility function to delay effects
function delay(millis) {
const promise = new Promise(resolve => {
setTimeout(() => resolve(true), millis)
});
return promise;
}
function pingServer(client, token) {
if(token) {
const timeout = new Promise((resolve, reject) => {
setTimeout(reject, config.networkStatusTimeout, 'request timed out');
});
return Promise
.race([timeout, client.post('/api/auth/refresh/', { token })]);
} else {
return Promise.reject({ error: 'No token in the store'})
}
}
// Fetch data every 20 seconds
function* pollData(context) {
try {
yield call(delay, config.networkStatusInterval);
const token = yield select(state => state.token);
const res = yield pingServer(context.client, token);
yield call(context.callback, true);
yield put({
type: types.AUTH_STORE_TOKEN_ASYNC,
token: res.token,
});
} catch (error) {
yield call(context.callback, false);
// if the error is that there is no token, then we know we have to wait for a login
if(error.error && error.error === 'No token in the store') {
yield take(types.AUTH_LOGIN_SUCCESS);
} else if (error.non_field_errors &&
error.non_field_errors &&
error.non_field_errors.length &&
error.non_field_errors.length > 0 &&
( error.non_field_errors[0] === 'Signature has expired.' ||
error.non_field_errors[0] === 'Refresh has expired.' )
) {
yield put({
type: types.AUTH_DEAUTHENTICATE
});
}
} finally {
if (yield cancelled()) {
// pollDate cancelled
// if there is a token : this was a LOGIN, set status to ok
// if there is no token : this was a LOGOUT, set status to ko and wait for login
const token = yield select(state => state.token);
if(token) {
yield call(context.callback, true);
} else {
yield call(context.callback, false);
yield take(types.AUTH_LOGIN_SUCCESS);
}
}
}
}
// Wait for successful response, then fire another request
// Cancel polling if user logs out or log in
function* watchPollData(context) {
while (true) {
yield race([
call(pollData, context),
take(types.AUTH_LOGOUT),
take(types.AUTH_LOGIN_SUCCESS)
]);
}
}
// Daemonize tasks in parallel
export default function* root(baseContext) {
const actionRes = yield take(types.OFFLINE_CONFIG_INITIALIZED);
const context = {...baseContext, ...actionRes.additionalContext};
yield all([
fork(watchPollData, context)
// other watchers here
]);
}