Skip to content

Instantly share code, notes, and snippets.

@iMerica
Last active April 2, 2019 01:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iMerica/9ef2c85303b0bfea1608d0ab5d78e8c3 to your computer and use it in GitHub Desktop.
Save iMerica/9ef2c85303b0bfea1608d0ab5d78e8c3 to your computer and use it in GitHub Desktop.
A Pattern for Auth based Routing using Redux, React Router

A Pattern for Auth based Routing using Redux, React Router

Assumptions

  • Token based authentication or JWT. This one is good for Django Rest Authentification.
  • The first line of defense in protecting sensitive data is your REST API and its auth system, not your React SPA. This solution is just for intelligent routing, not protecting sensitive data.
  • Redux or equivalent state management framework that allows you to easily connect components to a single source of truth state.

Summary

If you're familiar with the React concept of "lifting state up", then this will be a pretty familiar solution.

The core idea is to lift "auth business logic up". This means introducing a higher-level switch to your component heirachy that sits above the standard react router.

In this pattern, instead of having a single React Router instance, you have many:

  • One for totally unauthenticated visits (home page, login page).
  • One for regular authenticated users.
  • One for admin users.

Which one displays will depend on the initial auth request that determines authorization and authentication.

export const init = () => {
return {
type: TYPES.INITIAL_LOAD,
payload: {
token: window.localStorage.getItem('token')
}
}
}
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class AdminSwitch extends Component {
render() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Dashboard</Link>
</li>
<li>
<Link to="/account">Accounts</Link>
</li>
</ul>
<Route exact path="/" component={Dashboard} />
<Route path="/account" component={About} />
</div>
</Router>
);
}
}
import React, { Component } from 'react';
class App extends Component {
componentDidMount() {
this.props.init()
// other initializer actions
}
render() {
if (this.props.loggedIn && this.props.isAdmin)
return <AdminSwitch />
} else {
return <PublicSiteSwitch />
}
// Or Some other Routing based on your determined state.
}
}
const mapDispatchToProps = (dispatch) => {
return {
init: () => dispatch(init()),
// other intiializer actions
}
}
const mapStateToProps = (state) => {
return {
loggedIn: state.loggedIn,
isAdmin: state.isAdmin
}
}
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class PublicSiteSwitch extends Component {
render() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
);
}
}
export default PublicSiteSwitch;
import * as TYPES from '../types'
const intitialState = {
loading: false,
token: null,
loggedIn: false,
isAdmin: false,
}
export const rootReducer = (state = initialState, action) => {
switch (action.type) {
case TYPES.INITIAL_LOAD
return {
...state,
loggedIn: Boolean(action.payload.token),
token: action.payload.token,
}
default:
return state;
}
}
// other actions that dispatch ajax requests which return `isAdmin` type payloads
// ...
export const INITIAL_LOAD = 'INITIAL_LOAD';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment