1

I am having trouble understanding why my Authorized vs Unauthorized routes are not acting properly.. Here are the way my routes are set up:

class App extends Component {

  render() {
    return (
      <HashRouter>
          <React.Suspense fallback={loading()}>
            <Switch>
              <UnauthenticatedRoute exact path="/login" name="Login Page" component={Login} />
              <Route exact path="/register" name="Register Page" component={Register} />
              <Route exact path="/404" name="Page 404" component={Page404} />
              <Route exact path="/500" name="Page 500" component={Page500} />
              <AuthenticatedRoute path="/" name="Home" component={DefaultLayout} />
            </Switch>
          </React.Suspense>
      </HashRouter>
    );
  }
}

export default App;

I have an auth.js that holds all these route types as well as a check to see if the JWT token is valid:

import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { Api } from './api'

const isAuthenticated = () => {
  Api.isActiveToken(sessionStorage.getItem('token')).then(
    (response) => {
      console.log(response)
      return response.ok
    },
    (error) => {
      return false
    }
  )
}

const AuthenticatedRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={(props) => (
    isAuthenticated()
      ? <Component {...props} />
      : <Redirect to='/login' />
  )} />
);

const UnauthenticatedRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={(props) => (
    !isAuthenticated()
      ? <Component {...props} />
      : <Redirect to='/' />
  )} />
);

export { AuthenticatedRoute, UnauthenticatedRoute }

Where the console.log(response) looks as such:

Response {type: "cors", url: "http://xxx/api/v1/login/validate-token", redirected: false, status: 200, ok: true, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://xxx/api/v1/login/validate-token"
__proto__: Response

My sessionStorage is holding the token just fine. What am I doing wrong such that my route never redirects/allows me to the AuthorizedRoute?

sgerbhctim
  • 3,420
  • 7
  • 38
  • 60

2 Answers2

1

isAuthenticated() is asynchronous and currently returns void. You'll have to make isAuthenticated return a Promise of a boolean.

Once you have isAuthenticated returning a value, you'll need to use an effect and state to pull the actual value out of the promise.

import React, { useState, useEffect } from 'react';
import { Redirect, Route } from 'react-router-dom';
import { Api } from './api';

const isAuthenticated = async () => {
  try {
    const response = await Api.isActiveToken(sessionStorage.getItem('token'));
    return response.ok;
  } catch (error) {
    return false;
  }
};

const AuthenticatedRoute = ({ component: Component, ...rest }) => {
  const [authenticated, setAuthenticated] = useState(null);
  useEffect(() => {
    isAuthenticated().then((bool) => setAuthenticated(bool));
  }, []);

  return (
    <Route
      {...rest}
      render={(props) => {
        if (authenticated === null) return '...loading';
        return authenticated ? <Component {...props} /> : <Redirect to="/login" />;
      }}
    />
  );
};

const UnauthenticatedRoute = ({ component: Component, ...rest }) => {
  const [authenticated, setAuthenticated] = useState(null);
  useEffect(() => {
    isAuthenticated().then((bool) => setAuthenticated(bool));
  }, []);
  return (
    <Route
      {...rest}
      render={(props) => {
        if (authenticated === null) return '...loading';
        return !authenticated ? <Component {...props} /> : <Redirect to="/" />;
      }}
    />
  );
};

export { AuthenticatedRoute, UnauthenticatedRoute };
Zachary Haber
  • 10,376
  • 1
  • 17
  • 31
  • This results in: `Expected an assignment or function call and instead saw an expression` – sgerbhctim Apr 28 '20 at 05:08
  • I added in the return statements I had forgotten – Zachary Haber Apr 28 '20 at 05:11
  • Whoops, nevermind,.. this might have worked, 1 second! – sgerbhctim Apr 28 '20 at 05:14
  • Ok, so now when I logout using `sessionStorage.removeItem('token');`, I result in a continuous loop via `async` `await` cycle trying to call my backend – sgerbhctim Apr 28 '20 at 05:16
  • I had also accidentally left `!isAuthenticated` in the `UnauthenticatedRoute` instead of `! authenticated` which might have caused it to continuously redirect to "/", but other than that, I'm not seeing where the infinite loop is from. – Zachary Haber Apr 28 '20 at 05:28
  • Nope, we are all good, the `!authenticated` solved it! Thanks a bunch! Any chance you'd like to connect on GitHub or something? @mitchbregs is my username! I really really appreciate your expertise – sgerbhctim Apr 28 '20 at 05:29
0

I think the problem is that the Switch component expects certain types of child components, and you're passing it a different component type AuthenticatedRoute which it probably can't handle. Rather than making a new component type, you can convert your components to render functions that just return the Route element so that the Switch only contains routes.

const renderAuthenticatedRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={(props) => (
    isAuthenticated()
      ? <Component {...props} />
      : <Redirect to='/login' />
  )} />
);

const renderUnauthenticatedRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={(props) => (
    !isAuthenticated()
      ? <Component {...props} />
      : <Redirect to='/' />
  )} />
);

class App extends Component {
  render() {
    return (
      <HashRouter>
          <React.Suspense fallback={loading()}>
            <Switch>
              { 
                renderUnauthenticatedRoute({ 
                  exact: true, 
                  path: "/login", 
                  name: "Login Page",
                  component: Login
                })
              }
              <Route exact path="/register" name="Register Page" component={Register} />
              <Route exact path="/404" name="Page 404" component={Page404} />
              <Route exact path="/500" name="Page 500" component={Page500} />
              { 
                renderAuthenticatedRoute({
                  path: "/",
                  name: "Home",
                  component: DefaultLayout
                }) 
              }
            </Switch>
          </React.Suspense>
      </HashRouter>
    );
  }
}
Jacob
  • 77,566
  • 24
  • 149
  • 228