I'm trying to build role-based authentication using firebase, react and redux. Is it any way to add a custom property (such as 'permission' or 'role') to the firebase user object while creating new user from app level, or should I make it done some other way?
@UPDATE - problem resolved I built the role-based firebase authentication using custom claims as @frank-van-puffelen suggested. There are three roles: admin, worker and customer. Only admin is able to create worker accounts, customers are logging in via google+.
I used firebase cloud functions to create worker accounts, (because when i was trying to do this locally, i was automatically signed in with newly created account), then to set custom claims containing user's role. Cloud function is also decoding a token, which contains custom claims.
So here's my code snippets:
Cloud functions
const functions = require('firebase-functions');
const cors = require('cors');
const corsHandler = cors({origin: true});
const admin = require('firebase-admin');
admin.initializeApp();
exports.createWorkerAccount = functions.https.onRequest((request, response) => {
corsHandler(request, response, () => {
response.status(200).send('Hello from Cloud Function');
});
admin.auth().createUser({
email: request.body.email,
password: request.body.password
}).then(() => {
admin.auth().getUserByEmail(request.body.email).then((user) => {
admin.auth().setCustomUserClaims(user.uid, {role: 'worker'});
});
});
});
exports.getToken = functions.https.onRequest((request, response) => {
const token = admin.auth().verifyIdToken(request.body.token) //decoding token that contains custom claims
.then((t) => {
corsHandler(request, response, () => {
response.status(200).send(t.role); //sending role as a response
});
});
});
Both functions are called by simple ajax requests.
The user's role comes back as a request response, then it's pushed to redux state so I am able to access it from everywhere in the app.
To redirect logged in user to specific path and restrict it I'am using react-router public and private paths and also firebase function onAuthStateChanged.
Detecting firebase auth state changes - it's redirecting user to specific path based on custom claim.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
store.dispatch(login(user.uid));
firebase.auth().currentUser.getIdToken() //getting current user's token
.then((t) => {
getTokenRequest(t) // Promise containing the reqest
.then((role) => {
store.dispatch(permissions(role)); //dispatching role to store
renderApp();
if (role == "admin") {
console.log('admin');
history.push('/admin/dashboard'); //redirecting to user-role-specific path
}
else if (role == "worker") {
history.push('/worker/dashboard');
}
else {
history.push('/customer/dashboard');
}
});
});
} else {
store.dispatch(logout());
renderApp();
history.push('/');
}
});
React-router public route component which is disallowing the user to access wrong paths.
export const PublicRoute = ({
isAuthenticated,
role,
component: Component,
...rest
}) => (
<Route {...rest} component={(props) => {
if(isAuthenticated && role == "admin") {
return <Redirect to="/admin/dashboard" />
} else if (isAuthenticated && role == "") {
return <Redirect to ="/customer/dashboard" />
}
else {
return <Component {...props} />
}
}} />
);
const mapStateToProps = (state) => ({
isAuthenticated: !!state.auth.uid,
role: state.role
});
export default connect(mapStateToProps)(PublicRoute);