Frontend Authed Routes with react-router

Hi! I'm Ken.

I'm a full-stack product engineer.

I build dumb fun web stuff and work on rusty old cars.

I'm currently a Developer Advocate at Stream!

Stream lets you build scalable and personalized activity feeds.

If you'd like to revisit this talk later:

Slides: ken.hoff.tech/slides/frontend-authed-routes
Source code: github.com/kenhoff/frontend-authed-routes
Live demo:kenhoff.github.io/frontend-authed-routes

I'm working on Winds!

Winds is an RSS reader and podcast player that will showcase activity feeds from Stream.

electron + lots of different views + auth pages

Tonight we're learning about react-router

and how to build "frontend authenticated routes" with it

What you should know for this talk

Building frontend authed routes with react-router

  1. react-router theory, example, and practice
  2. The case for frontend authenticated routes
  3. The Route component - path, component, and render props
  4. Authed and Unauthed Routes
  5. Reusable AuthedRoute and UnauthedRoute components

What react-router does

Renders different React components based on window location

Change page content with no refreshes

Quick demo: kenhoff.github.io/frontend-authed-routes/

Docs: reacttraining.com/react-router/

A simple example with react-router

<Router history={createBrowserHistory()}>
    <div>
        <ul>
            <li><Link to="/home">Home</Link></li>
            <li><Link to="/pricing">Pricing</Link></li>
            <li><Link to="/about">About</Link></li>
        </ul>
        <Route path="/home" component={Home}></Route>
        <Route path="/pricing" component={Pricing}></Route>
        <Route path="/about" component={About}></Route>
    </div>
</Router>

Need to bench test? create-react-app is probably the easiest

react-router components

Frontend authenticated routes? psh, who needs that?

Just reload the page!

unauthed user → /my-account → hard redirect to /sign-in

unauthed user → /sign-in → authenticates → hard redirect to /my-account

Frontend authed routes == No hard redirects when authenticating

nice to have in web apps, but really important with desktop/native/progressive/electron apps

Redirects:

UsersSourceDestination
All users/homestay put :)
All users/pricingstay put :)
All users/aboutstay put :)
Unauthed users/my-settings/sign-in
Authed users/sign-in/my-settings

The approach

"Authed Route" - only render the component if the user is authenticated (otherwise, redirect)

"Unauthed Route" - only render the component if the user is not authenticated (otherwise, redirect)

If we don't care if the user is authed or not, just use plain Route

Maybe Route has something built in for this?

The Route component

The easy props:

paththe URL to match (can include route params, like with express)
componentthe component to render if the path matches (can include nested routes!)
render (function)called if the path matches (return a React component or element)
<Route path="/pricing" component={Pricing} />

<Route path="/about" render={() => {
    return (<About />);
}} />

An "Authed Route", theory

  1. use the render prop (only called when path matches)
  2. in render, check to see if the user is authenticated or not
  3. if user is authenticated, render the component
  4. if user is not authenticated, render a Redirect to /sign-in

An "Authed Route", practice

<Route path="/my-settings" render={(props) => {
    if (userIsAuthenticated()) {
        return (<Settings {...props}></Settings>);
    } else {
        return (<Redirect to="/sign-in"></Redirect>)
    }
    }}></Route>

(why do we have to include ...props ?)

reacttraining.com/react-router/web/api/Route/route-props

An "Unauthed Route"

(basically the opposite of our "Authed Route")

<Route path="/sign-in" render={(props) => {
    if (userIsAuthenticated()) {
        return (<Redirect to="/my-settings"></Redirect>)
    } else {
        return (<SignIn {...props}></SignIn>);
    }
    }}></Route>

Reuse it!

I want a bunch of authed and unauthed routes -

Want something reusable and easy-to-read

Something like "AuthedRoute" and "UnauthedRoute"

Still want to use Route for routes that we don't care about auth on

Our AuthedRoute component

putting it all together....

  1. new AuthedRoute component
  2. AuthedRoute renders a Route
  3. that uses the render prop (only called when path matches)
  4. check to see if the user is authenticated or not
  5. if the user is authenticated, render the component (passed in as props)
  6. if the user is not authenticated, render a Redirect to /sign-in

composition yay!

Our AuthedRoute component

    let AuthedRoute = ({
        component: Component,
        ...rest
    }) => {
        return <Route {...rest} render={(props) => {
            if (userIsAuthenticated()) {
                return (<Component {...props}></Component>);
            } else {
                return (<Redirect to="/sign-in"></Redirect>)
            }
            }}></Route>
    }

    <AuthedRoute path="/my-settings" component={Settings}></AuthedRoute>

Our UnauthedRoute component

....is basically the opposite of the AuthedRoute component

    let UnauthedRoute = ({
        component: Component,
        ...rest
    }) => {
        return <Route {...rest} render={(props) => {
            if (userIsAuthenticated()) {
                return (<Redirect to="/my-settings"></Redirect>)
            } else {
                return (<Component {...props}></Component>);
            }
            }}></Route>
    }

    <UnauthedRoute path="/sign-in" component={SignIn}></UnauthedRoute>

The finished Router

<Router history={createBrowserHistory()}>
    <div>
        <ul>
            <li><Link to="/home">Home</Link></li>
            <li><Link to="/pricing">Pricing</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/my-settings">My settings (authed)</Link></li>
            <li><Link to="/sign-in">Sign in (unauthed)</Link></li>
        </ul>
        <Route path="/home" component={Home}></Route>
        <Route path="/pricing" component={Pricing}></Route>
        <Route path="/about" component={About}></Route>
        <AuthedRoute path="/my-settings" component={Settings}></AuthedRoute>
        <UnauthedRoute path="/sign-in" component={SignIn}></UnauthedRoute>
    </div>
</Router>

Exercises for the reader

Thanks so much!

ken_hoff