mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-01-17 00:52:50 +05:30
#232: fix remember me logic on frontend
This commit is contained in:
parent
70ae13ae84
commit
9da79a15b4
@ -49,6 +49,11 @@ export function authenticate({token, refreshToken}) {
|
|||||||
...user
|
...user
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (!account.refreshToken) {
|
||||||
|
// mark user as stranger (user does not want us to remember his account)
|
||||||
|
sessionStorage.setItem(`stranger${account.id}`, 1);
|
||||||
|
}
|
||||||
|
|
||||||
return dispatch(setLocale(user.lang))
|
return dispatch(setLocale(user.lang))
|
||||||
.then(() => account);
|
.then(() => account);
|
||||||
});
|
});
|
||||||
@ -75,6 +80,48 @@ export function revoke(account) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function logoutAll() {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const {accounts: {available}} = getState();
|
||||||
|
|
||||||
|
available.forEach((account) => authentication.logout(account));
|
||||||
|
|
||||||
|
dispatch(reset());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logouts accounts, that was marked as "do not remember me"
|
||||||
|
*
|
||||||
|
* We detecting foreign accounts by the absence of refreshToken. The account
|
||||||
|
* won't be removed, until key `stranger${account.id}` is present in sessionStorage
|
||||||
|
*
|
||||||
|
* @return {function}
|
||||||
|
*/
|
||||||
|
export function logoutStrangers() {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const {accounts: {available}} = getState();
|
||||||
|
|
||||||
|
const isStranger = ({refreshToken, id}) => !refreshToken && !sessionStorage.getItem(`stranger${id}`);
|
||||||
|
|
||||||
|
const accountToReplace = available.filter((account) => !isStranger(account))[0];
|
||||||
|
|
||||||
|
if (accountToReplace) {
|
||||||
|
available.filter(isStranger)
|
||||||
|
.forEach((account) => {
|
||||||
|
dispatch(remove(account));
|
||||||
|
authentication.logout(account);
|
||||||
|
});
|
||||||
|
|
||||||
|
return dispatch(authenticate(accountToReplace));
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(logout());
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const ADD = 'accounts:add';
|
export const ADD = 'accounts:add';
|
||||||
/**
|
/**
|
||||||
* @api private
|
* @api private
|
||||||
@ -120,15 +167,6 @@ export function activate(account) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logoutAll() {
|
|
||||||
return (dispatch, getState) => {
|
|
||||||
const {accounts: {available}} = getState();
|
|
||||||
|
|
||||||
available.forEach((account) => authentication.logout(account));
|
|
||||||
|
|
||||||
dispatch(reset());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export const RESET = 'accounts:reset';
|
export const RESET = 'accounts:reset';
|
||||||
/**
|
/**
|
||||||
* @api private
|
* @api private
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { changeLang } from 'components/user/actions';
|
import { changeLang } from 'components/user/actions';
|
||||||
import { authenticate } from 'components/accounts/actions';
|
import { authenticate, logoutStrangers } from 'components/accounts/actions';
|
||||||
|
|
||||||
import request from 'services/request';
|
import request from 'services/request';
|
||||||
import bearerHeaderMiddleware from './middlewares/bearerHeaderMiddleware';
|
import bearerHeaderMiddleware from './middlewares/bearerHeaderMiddleware';
|
||||||
@ -22,22 +22,25 @@ export function factory(store) {
|
|||||||
request.addMiddleware(refreshTokenMiddleware(store));
|
request.addMiddleware(refreshTokenMiddleware(store));
|
||||||
request.addMiddleware(bearerHeaderMiddleware(store));
|
request.addMiddleware(bearerHeaderMiddleware(store));
|
||||||
|
|
||||||
promise = Promise.resolve().then(() => {
|
promise = Promise.resolve()
|
||||||
const {user, accounts} = store.getState();
|
.then(() => store.dispatch(logoutStrangers()))
|
||||||
|
.then(() => {
|
||||||
|
const {user, accounts} = store.getState();
|
||||||
|
|
||||||
if (accounts.active || user.token) {
|
if (accounts.active || user.token) {
|
||||||
// authorizing user if it is possible
|
// authorizing user if it is possible
|
||||||
return store.dispatch(authenticate(accounts.active || user));
|
return store.dispatch(authenticate(accounts.active || user));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}).catch(() => {
|
})
|
||||||
// the user is guest or user authentication failed
|
.catch(() => {
|
||||||
const {user} = store.getState();
|
// the user is guest or user authentication failed
|
||||||
|
const {user} = store.getState();
|
||||||
|
|
||||||
// auto-detect guest language
|
// auto-detect guest language
|
||||||
return store.dispatch(changeLang(user.lang));
|
return store.dispatch(changeLang(user.lang));
|
||||||
});
|
});
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import expect from 'unexpected';
|
import expect from 'unexpected';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import accounts from 'services/api/accounts';
|
import accounts from 'services/api/accounts';
|
||||||
import authentication from 'services/api/authentication';
|
import authentication from 'services/api/authentication';
|
||||||
@ -9,7 +10,8 @@ import {
|
|||||||
activate, ACTIVATE,
|
activate, ACTIVATE,
|
||||||
remove,
|
remove,
|
||||||
reset,
|
reset,
|
||||||
logoutAll
|
logoutAll,
|
||||||
|
logoutStrangers
|
||||||
} from 'components/accounts/actions';
|
} from 'components/accounts/actions';
|
||||||
import { SET_LOCALE } from 'components/i18n/actions';
|
import { SET_LOCALE } from 'components/i18n/actions';
|
||||||
|
|
||||||
@ -114,6 +116,20 @@ describe('components/accounts/actions', () => {
|
|||||||
expect(dispatch, 'was not called')
|
expect(dispatch, 'was not called')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('marks user as stranger, if there is no refreshToken', () => {
|
||||||
|
const expectedKey = `stranger${account.id}`;
|
||||||
|
authentication.validateToken.returns(Promise.resolve({
|
||||||
|
token: account.token
|
||||||
|
}));
|
||||||
|
|
||||||
|
sessionStorage.removeItem(expectedKey);
|
||||||
|
|
||||||
|
return authenticate(account)(dispatch).then(() => {
|
||||||
|
expect(sessionStorage.getItem(expectedKey), 'not to be null')
|
||||||
|
sessionStorage.removeItem(expectedKey);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#revoke()', () => {
|
describe('#revoke()', () => {
|
||||||
@ -242,4 +258,110 @@ describe('components/accounts/actions', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#logoutStrangers', () => {
|
||||||
|
const foreignAccount = {
|
||||||
|
...account,
|
||||||
|
id: 2,
|
||||||
|
refreshToken: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
const foreignAccount2 = {
|
||||||
|
...foreignAccount,
|
||||||
|
id: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getState.returns({
|
||||||
|
accounts: {
|
||||||
|
active: account,
|
||||||
|
available: [account, foreignAccount, foreignAccount2]
|
||||||
|
},
|
||||||
|
user
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.stub(authentication, 'logout').named('authentication.logout');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
authentication.logout.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove stranger accounts', () => {
|
||||||
|
logoutStrangers()(dispatch, getState);
|
||||||
|
|
||||||
|
expect(dispatch, 'to have a call satisfying', [
|
||||||
|
remove(foreignAccount)
|
||||||
|
]);
|
||||||
|
expect(dispatch, 'to have a call satisfying', [
|
||||||
|
remove(foreignAccount2)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should logout stranger accounts', () => {
|
||||||
|
logoutStrangers()(dispatch, getState);
|
||||||
|
|
||||||
|
expect(authentication.logout, 'to have calls satisfying', [
|
||||||
|
[foreignAccount],
|
||||||
|
[foreignAccount2]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should activate another account if available', () =>
|
||||||
|
logoutStrangers()(dispatch, getState)
|
||||||
|
.then(() =>
|
||||||
|
expect(dispatch, 'to have a call satisfying', [
|
||||||
|
activate(account)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('when all accounts are strangers', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
getState.returns({
|
||||||
|
accounts: {
|
||||||
|
active: foreignAccount,
|
||||||
|
available: [foreignAccount, foreignAccount2]
|
||||||
|
},
|
||||||
|
user
|
||||||
|
});
|
||||||
|
|
||||||
|
logoutStrangers()(dispatch, getState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logouts all accounts', () => {
|
||||||
|
expect(dispatch, 'to have a call satisfying', [
|
||||||
|
{payload: {isGuest: true}}
|
||||||
|
// updateUser({isGuest: true})
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(dispatch, 'to have a call satisfying', [
|
||||||
|
reset()
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when an stranger has a mark in sessionStorage', () => {
|
||||||
|
const key = `stranger${foreignAccount.id}`;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sessionStorage.setItem(key, 1);
|
||||||
|
|
||||||
|
logoutStrangers()(dispatch, getState);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
sessionStorage.removeItem(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not log out', () =>
|
||||||
|
expect(dispatch, 'to have calls satisfying', [
|
||||||
|
[expect.it('not to equal', {payload: foreignAccount})],
|
||||||
|
// for some reason it says, that dispatch(authenticate(...))
|
||||||
|
// must be removed if only one args assertion is listed :(
|
||||||
|
[expect.it('not to equal', {payload: foreignAccount})]
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ expect.use(require('unexpected-sinon'));
|
|||||||
if (!window.localStorage) {
|
if (!window.localStorage) {
|
||||||
window.localStorage = {
|
window.localStorage = {
|
||||||
getItem(key) {
|
getItem(key) {
|
||||||
return this[key];
|
return this[key] || null;
|
||||||
},
|
},
|
||||||
setItem(key, value) {
|
setItem(key, value) {
|
||||||
this[key] = value;
|
this[key] = value;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user