Skip to content

Instantly share code, notes, and snippets.

@GiladShoham
Created April 3, 2017 09:00
Show Gist options
  • Save GiladShoham/f9176ccc9d4385499ed1568feef18d41 to your computer and use it in GitHub Desktop.
Save GiladShoham/f9176ccc9d4385499ed1568feef18d41 to your computer and use it in GitHub Desktop.
React Authorizaion component
import React, { PropTypes } from 'react';
import apConnect from 'apollo-passportjs-react/lib/connect';
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
const Authorization = (allowedRoles) =>
(WrappedComponent, options = {}) => {
const AuthorizationDisplayName = 'WithAuthorization('
+ getDisplayName(WrappedComponent) + ')';
class WithAuthorization extends React.Component {
constructor(props) {
super(props)
this.state = {
isAuthorized: false,
}
}
componentWillMount() {
const auth = this.props.auth;
if (!_isAuthenticated(auth)){
this.context.router.replace('/login');
return;
}
const { role } = auth.data;
if (allowedRoles.includes(role)) {
this.setState({isAuthorized: true});
} else if (options.redirectTo){
this.context.router.replace(options.redirectTo);
}
}
render() {
if (this.state.isAuthorized) {
return <WrappedComponent {...this.props} />;
} else {
return null;
}
}
}
WithAuthorization.contextTypes = {
router: React.PropTypes.object
}
WithAuthorization.displayName = AuthorizationDisplayName;
const authData = PropTypes.shape({
displayName: PropTypes.string.isRequired,
exp: PropTypes.number.isRequired,
iat: PropTypes.number.isRequired,
role: PropTypes.string.isRequired,
userId: PropTypes.string.isRequired,
});
const auth = PropTypes.shape({
data: authData.isRequired,
error: PropTypes.string,
verified: PropTypes.bool.isRequired,
});
WithAuthorization.propTypes = {
auth: auth.isRequired,
};
return apConnect(
({ auth }) => ({ auth: auth})
)(WithAuthorization);
}
export default Authorization;
const ManagerOnly = Authorization(['MANAGER', 'ADMIN', 'SUPER']);
const AdminOnly = Authorization(['ADMIN', 'SUPER']);
export {
ManagerOnly,
AdminOnly
}
const _isAuthenticated = (auth) => {
if (!auth || !auth.data || !auth.data.role || auth.error){
return false;
}
const expiration = new Date(auth.data.exp * 1000);
const expired = _isExpired(expiration)
return !(expired);
}
const _isExpired = (expiration) => {
const now = new Date();
return (now > expiration)
}
import {AdminOnly, ManagerOnly} from 'Authorization';
export default (
<div>
<Route path="/" component={Base}>
{ /* A route only admin can visit */ }
<Route path="admin" component={AdminOnly(AdminDashboard, {redirectTo: '/home'})}/>
{ /* A route only manager (and admins) can visit */ }
<Route path="manager" component={ManagerOnly(ManagerDashboard, {redirectTo: '/home'})}/>
</Route>
);
{ /* Generate a sidbebar menu where admin can see both menu items admin and manager and manager can see only the manager menu item*/ }
import Authorization from '../Common/Authorization';
const menuItems = [{
label: 'Admin',
allowedRoles: ['ADMIN', 'SUPER'],
route: 'admin',
},{
label: 'Manager',
allowedRoles: ['ADMIN', 'SUPER', 'MANAGER'],
route: 'manager',
},
class Sidebar extends React.Component {
render() {
const menuItemsJsx = menuItems.map((menuItem) =>{
const allowedRoles = menuItem.allowedRoles;
const SidebarMenuItemWithAuth = allowedRoles ? Authorization(allowedRoles)(SidebarMenuItem) : SidebarMenuItem;
return (<SidebarMenuItemWithAuth key={menuItem.label} label={menuItem.label} route={menuItem.route} />)
}
)
return (
<div>
{menuItemsJsx}
</div>
)
}
@paulewetzel
Copy link

Someone came up with almost the exact solution you did 2 years ago... https://hackernoon.com/role-based-authorization-in-react-c70bb7641db4 but not as complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment