Skip to content

Commit b630175

Browse files
committed
implementation of redux-auth-wrapper
1 parent 0ac5c83 commit b630175

3 files changed

Lines changed: 80 additions & 36 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
}
9999
},
100100
"dependencies": {
101+
"async": "^2.1.4",
101102
"babel-core": "^6.21.0",
102103
"babel-plugin-add-module-exports": "^0.2.1",
103104
"babel-plugin-transform-decorators-legacy": "^1.3.4",
@@ -148,6 +149,7 @@
148149
"react-router-redux": "^4.0.7",
149150
"react-router-scroll": "^0.4.1",
150151
"redux": "^3.6.0",
152+
"redux-auth-wrapper": "^0.9.0",
151153
"redux-connect": "^5.0.0",
152154
"redux-form": "^6.3.2",
153155
"redux-persist": "^4.0.0-beta2",

src/containers/App/App.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { asyncConnect } from 'redux-connect';
3636
export default class App extends Component {
3737
static propTypes = {
3838
children: PropTypes.object.isRequired,
39+
router: PropTypes.object.isRequired,
3940
user: PropTypes.object,
4041
notifs: PropTypes.object.isRequired,
4142
logout: PropTypes.func.isRequired,
@@ -53,7 +54,8 @@ export default class App extends Component {
5354
componentWillReceiveProps(nextProps) {
5455
if (!this.props.user && nextProps.user) {
5556
// login
56-
this.props.pushState('/loginSuccess');
57+
const redirect = this.props.router.location.query && this.props.router.location.query.redirect;
58+
this.props.pushState(redirect || '/loginSuccess');
5759
} else if (this.props.user && !nextProps.user) {
5860
// logout
5961
this.props.pushState('/');

src/routes.js

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,79 @@
11
import React from 'react';
22
import { IndexRoute, Route } from 'react-router';
3+
import { routerActions } from 'react-router-redux';
4+
import { UserAuthWrapper } from 'redux-auth-wrapper';
5+
import async from 'async';
6+
import isPromise from 'is-promise';
37
import { isLoaded as isAuthLoaded, load as loadAuth } from 'redux/modules/auth';
48
import { App, Home, NotFound } from 'containers';
59

610
// eslint-disable-next-line import/no-dynamic-require
711
if (typeof System.import === 'undefined') System.import = module => Promise.resolve(require(module));
812

913
export default store => {
10-
const loadAuthIfNeeded = cb => {
14+
const injectReducerAndRender = (name, reducerPromise, containerPromise) =>
15+
Promise.all([reducerPromise, containerPromise])
16+
.then(([reducer, container]) => {
17+
store.inject(name, reducer.default || reducer);
18+
return container.default || container;
19+
});
20+
21+
const onEnterChain = (...listOfOnEnters) => (nextState, replace, onEnterCb) => {
22+
let redirected = false;
23+
const wrappedReplace = (...args) => {
24+
replace(...args);
25+
redirected = true;
26+
};
27+
async.eachSeries(listOfOnEnters, (onEnter, callback) => {
28+
if (!redirected) {
29+
const result = onEnter(store, nextState, wrappedReplace);
30+
if (isPromise(result)) return result.then(() => callback(), callback);
31+
}
32+
callback();
33+
}, err => {
34+
if (err) onEnterCb(err);
35+
onEnterCb();
36+
});
37+
};
38+
39+
const loadAuthIfNeeded = () => {
1140
if (!isAuthLoaded(store.getState())) {
12-
return store.dispatch(loadAuth()).then(() => cb()).catch(cb);
41+
return store.dispatch(loadAuth()).catch(() => {});
1342
}
14-
return cb();
15-
};
16-
const checkUser = (cond, replace, cb) => {
17-
const { auth: { user } } = store.getState();
18-
if (!cond(user)) replace('/');
19-
cb();
43+
return Promise.resolve();
2044
};
2145

22-
const requireNotLogged = (nextState, replace, cb) => {
23-
const cond = user => !user;
24-
loadAuthIfNeeded(() => checkUser(cond, replace, cb));
25-
};
26-
const requireLogin = (nextState, replace, cb) => {
27-
const cond = user => !!user;
28-
loadAuthIfNeeded(() => checkUser(cond, replace, cb));
46+
const checkPermissions = chainedPermissions => loadAuthIfNeeded().then(() => chainedPermissions);
47+
48+
const enterPermissions = (...listOfPermissions) => {
49+
const permissions = [loadAuthIfNeeded].concat(listOfPermissions.map(perm => perm.onEnter || perm));
50+
return onEnterChain(...permissions);
2951
};
3052

31-
const injectAndRender = (name, reducerPromise, containerPromise) =>
32-
Promise.all([reducerPromise, containerPromise])
33-
.then(([reducer, container]) => {
34-
store.inject(name, reducer.default || reducer);
35-
return container.default || container;
36-
});
53+
const permissionsComponent = (...listOfPermissions) => ({
54+
onEnter: enterPermissions(...listOfPermissions),
55+
getComponent: () => checkPermissions(listOfPermissions.reduceRight(
56+
(prev, next) => next(prev),
57+
props => props.children
58+
))
59+
});
60+
61+
/* Permissions */
62+
63+
const isAuthenticated = UserAuthWrapper({
64+
authSelector: state => state.auth.user,
65+
redirectAction: routerActions.replace,
66+
wrapperDisplayName: 'UserIsAuthenticated'
67+
});
68+
69+
const isNotAuthenticated = UserAuthWrapper({
70+
authSelector: state => state.auth.user,
71+
redirectAction: routerActions.replace,
72+
wrapperDisplayName: 'UserIsNotAuthenticated',
73+
predicate: user => !user,
74+
failureRedirectPath: '/',
75+
allowRedirectBack: false
76+
});
3777

3878
/**
3979
* Please keep routes in alphabetical order
@@ -44,19 +84,19 @@ export default store => {
4484
<IndexRoute component={Home} />
4585

4686
{/* Routes requiring login */}
47-
<Route onEnter={requireLogin}>
87+
<Route {...permissionsComponent(isAuthenticated)}>
4888
<Route path="loginSuccess" getComponent={() => System.import('./containers/LoginSuccess/LoginSuccess')} />
4989
<Route
5090
path="chatFeathers"
51-
getComponent={() => injectAndRender(
52-
'chat',
53-
System.import('./redux/modules/chat'),
91+
getComponent={() => injectReducerAndRender(
92+
'chat', System.import('./redux/modules/chat'),
5493
System.import('./containers/ChatFeathers/ChatFeathers')
55-
)} />
94+
)}
95+
/>
5696
</Route>
5797

5898
{/* Routes disallow login */}
59-
<Route onEnter={requireNotLogged}>
99+
<Route {...permissionsComponent(isNotAuthenticated)}>
60100
<Route path="register" getComponent={() => System.import('./containers/Register/Register')} />
61101
</Route>
62102

@@ -65,18 +105,18 @@ export default store => {
65105
<Route path="about" getComponent={() => System.import('./containers/About/About')} />
66106
<Route
67107
path="survey"
68-
getComponent={() => injectAndRender(
69-
'survey',
70-
System.import('./redux/modules/survey'),
108+
getComponent={() => injectReducerAndRender(
109+
'survey', System.import('./redux/modules/survey'),
71110
System.import('./containers/Survey/Survey')
72-
)} />
111+
)}
112+
/>
73113
<Route
74114
path="widgets"
75-
getComponent={() => injectAndRender(
76-
'widgets',
77-
System.import('./redux/modules/widgets'),
115+
getComponent={() => injectReducerAndRender(
116+
'widgets', System.import('./redux/modules/widgets'),
78117
System.import('./containers/Widgets/Widgets')
79-
)} />
118+
)}
119+
/>
80120
<Route path="chat" getComponent={() => System.import('./containers/Chat/Chat')} />
81121

82122
{/* Catch all route */}

0 commit comments

Comments
 (0)