Skip to content

Commit 6188752

Browse files
committed
14-React Firebase Authorization with Roles
1 parent a20ec71 commit 6188752

File tree

8 files changed

+93
-24
lines changed

8 files changed

+93
-24
lines changed

src/components/Account/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import React from 'react';
22

3-
import { AuthUserContext } from '../Session';
3+
import { AuthUserContext, withAuthorization } from '../Session';
44
import { PasswordForgetForm } from '../PasswordForget';
55
import PasswordChangeForm from '../PasswordChange';
6-
import { withAuthorization } from '../Session';
76

87
const AccountPage = () => (
98
<AuthUserContext.Consumer>
@@ -17,6 +16,6 @@ const AccountPage = () => (
1716
</AuthUserContext.Consumer>
1817
);
1918

20-
const authCondition = authUser => !!authUser;
19+
const condition = authUser => !!authUser;
2120

22-
export default withAuthorization(authCondition)(AccountPage);
21+
export default withAuthorization(condition)(AccountPage);

src/components/Admin/index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import React, { Component } from 'react';
2+
import { compose } from 'recompose';
23

34
import { withFirebase } from '../Firebase';
5+
import { withAuthorization } from '../Session';
6+
import * as ROLES from '../../constants/roles';
47

58
class AdminPage extends Component {
69
constructor(props) {
@@ -40,6 +43,9 @@ class AdminPage extends Component {
4043
return (
4144
<div>
4245
<h1>Admin</h1>
46+
<p>
47+
The Admin Page is accessible by every signed in admin user.
48+
</p>
4349

4450
{loading && <div>Loading ...</div>}
4551

@@ -67,4 +73,10 @@ const UserList = ({ users }) => (
6773
</ul>
6874
);
6975

70-
export default withFirebase(AdminPage);
76+
const condition = authUser =>
77+
authUser && authUser.roles.includes(ROLES.ADMIN);
78+
79+
export default compose(
80+
withAuthorization(condition),
81+
withFirebase,
82+
)(AdminPage);

src/components/Firebase/firebase.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,35 @@ class Firebase {
3434
doPasswordUpdate = password =>
3535
this.auth.currentUser.updatePassword(password);
3636

37+
// *** Merge Auth and DB User API *** //
38+
39+
onAuthUserListener = (next, fallback) =>
40+
this.auth.onAuthStateChanged(authUser => {
41+
if (authUser) {
42+
this.user(authUser.uid)
43+
.once('value')
44+
.then(snapshot => {
45+
const dbUser = snapshot.val();
46+
47+
// default empty roles
48+
if (!dbUser.roles) {
49+
dbUser.roles = [];
50+
}
51+
52+
// merge auth and db user
53+
authUser = {
54+
uid: authUser.uid,
55+
email: authUser.email,
56+
...dbUser,
57+
};
58+
59+
next(authUser);
60+
});
61+
} else {
62+
fallback();
63+
}
64+
});
65+
3766
// *** User API ***
3867

3968
user = uid => this.db.ref(`users/${uid}`);

src/components/Navigation/index.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import React from 'react';
22
import { Link } from 'react-router-dom';
33

4+
import { AuthUserContext } from '../Session';
45
import SignOutButton from '../SignOut';
56
import * as ROUTES from '../../constants/routes';
6-
7-
import { AuthUserContext } from '../Session';
7+
import * as ROLES from '../../constants/roles';
88

99
const Navigation = () => (
10-
<div>
11-
<AuthUserContext.Consumer>
12-
{authUser =>
13-
authUser ? <NavigationAuth /> : <NavigationNonAuth />
14-
}
15-
</AuthUserContext.Consumer>
16-
</div>
10+
<AuthUserContext.Consumer>
11+
{authUser =>
12+
authUser ? (
13+
<NavigationAuth authUser={authUser} />
14+
) : (
15+
<NavigationNonAuth />
16+
)
17+
}
18+
</AuthUserContext.Consumer>
1719
);
1820

19-
const NavigationAuth = () => (
21+
const NavigationAuth = ({ authUser }) => (
2022
<ul>
2123
<li>
2224
<Link to={ROUTES.LANDING}>Landing</Link>
@@ -27,9 +29,11 @@ const NavigationAuth = () => (
2729
<li>
2830
<Link to={ROUTES.ACCOUNT}>Account</Link>
2931
</li>
30-
<li>
31-
<Link to={ROUTES.ADMIN}>Admin</Link>
32-
</li>
32+
{authUser.roles.includes(ROLES.ADMIN) && (
33+
<li>
34+
<Link to={ROUTES.ADMIN}>Admin</Link>
35+
</li>
36+
)}
3337
<li>
3438
<SignOutButton />
3539
</li>

src/components/Session/withAuthentication.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ const withAuthentication = Component => {
1414
}
1515

1616
componentDidMount() {
17-
this.listener = this.props.firebase.auth.onAuthStateChanged(
17+
this.listener = this.props.firebase.onAuthUserListener(
1818
authUser => {
19-
authUser
20-
? this.setState({ authUser })
21-
: this.setState({ authUser: null });
19+
this.setState({ authUser });
20+
},
21+
() => {
22+
this.setState({ authUser: null });
2223
},
2324
);
2425
}

src/components/Session/withAuthorization.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import * as ROUTES from '../../constants/routes';
99
const withAuthorization = condition => Component => {
1010
class WithAuthorization extends React.Component {
1111
componentDidMount() {
12-
this.listener = this.props.firebase.auth.onAuthStateChanged(
12+
this.listener = this.props.firebase.onAuthUserListener(
1313
authUser => {
1414
if (!condition(authUser)) {
1515
this.props.history.push(ROUTES.SIGN_IN);
1616
}
1717
},
18+
() => this.props.history.push(ROUTES.SIGN_IN),
1819
);
1920
}
2021

src/components/SignUp/index.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Link, withRouter } from 'react-router-dom';
33

44
import { withFirebase } from '../Firebase';
55
import * as ROUTES from '../../constants/routes';
6+
import * as ROLES from '../../constants/roles';
67

78
const SignUpPage = () => (
89
<div>
@@ -16,6 +17,7 @@ const INITIAL_STATE = {
1617
email: '',
1718
passwordOne: '',
1819
passwordTwo: '',
20+
isAdmin: false,
1921
error: null,
2022
};
2123

@@ -27,7 +29,12 @@ class SignUpFormBase extends Component {
2729
}
2830

2931
onSubmit = event => {
30-
const { username, email, passwordOne } = this.state;
32+
const { username, email, passwordOne, isAdmin } = this.state;
33+
const roles = [];
34+
35+
if (isAdmin) {
36+
roles.push(ROLES.ADMIN);
37+
}
3138

3239
this.props.firebase
3340
.doCreateUserWithEmailAndPassword(email, passwordOne)
@@ -38,6 +45,7 @@ class SignUpFormBase extends Component {
3845
.set({
3946
username,
4047
email,
48+
roles,
4149
})
4250
.then(() => {
4351
this.setState({ ...INITIAL_STATE });
@@ -58,12 +66,17 @@ class SignUpFormBase extends Component {
5866
this.setState({ [event.target.name]: event.target.value });
5967
};
6068

69+
onChangeCheckbox = event => {
70+
this.setState({ [event.target.name]: event.target.checked });
71+
};
72+
6173
render() {
6274
const {
6375
username,
6476
email,
6577
passwordOne,
6678
passwordTwo,
79+
isAdmin,
6780
error,
6881
} = this.state;
6982

@@ -103,6 +116,15 @@ class SignUpFormBase extends Component {
103116
type="password"
104117
placeholder="Confirm Password"
105118
/>
119+
<label>
120+
Admin:
121+
<input
122+
name="isAdmin"
123+
type="checkbox"
124+
checked={isAdmin}
125+
onChange={this.onChangeCheckbox}
126+
/>
127+
</label>
106128
<button disabled={isInvalid} type="submit">
107129
Sign Up
108130
</button>

src/constants/roles.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const ADMIN = 'ADMIN';

0 commit comments

Comments
 (0)