Home > OS >  Invoke logout function when route changes
Invoke logout function when route changes

Time:12-02

How can I logout user when the JWT accessToken expires?

Right now I am using withRouter as a Higher order component(HOC) to check for route change, but it only works when the user clicks the back button of browser.

I want to check for route changes at every route change not just back button. for example, when the user clicks back to home link, I want to check if the token has expired and then remove token from local Storage.

AuthVerify.js

import React, { useEffect } from "react";
import { withRouter } from "react-router-dom";

const parseJwt = (token) => {
  try {
    return JSON.parse(atob(token.split(".")[1]));
  } catch (e) {
    return null;
  }
};

const AuthVerify = (props) => {
  console.log(props);
  useEffect(() => {
    props.history.listen(() => {
      const token = localStorage.getItem("accessToken");
      if (token) {
        const decodedJWT = parseJwt(token);

        if (decodedJWT.exp * 1000 < Date.now()) {
          console.log("expired");
          props.logOut();
        } else {
        }
      }
    });
  }, []);

  return <div></div>;
};

export default withRouter(AuthVerify);

Part of App.js

import { Fragment } from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";

import AuthVerify from "../src/utils/AuthVerify";

import "./App.css";

import Home from "./pages/Home";
import Signup from "../src/pages/Signup";
import Login from "../src/pages/Login";
import Test from "./pages/Test";

const logOut = () => {
  localStorage.removeItem("accessToken");
  localStorage.removeItem("refreshToken");
};

function App() {
  return (
    <Fragment>
      <div className="App">
        <BrowserRouter>
          <Switch>
            <Route exact path="/" component={Home} />
            <PrivateRoute exact path="/test" component={Test} />
            <Route exact path="/register" component={Signup} />
            <Route exact path="/login" component={Login} />
          </Switch>
        </BrowserRouter>
      </div>
      <AuthVerify logOut={logOut} />
    </Fragment>
  );
}

export default App;

test page with accessToken

after accessToken expired redirect to login page when route changes

CodePudding user response:

I don't see an issue with your AuthVerify, it should be called when the location changes. From what I can tell though is that you are rendering the AuthVerify component outside the router rendering the routes that are changing. The AuthVerify component needs to be rendered within the router so it's working with the same routing context handling the changing routes.

function App() {
  return (
    <Fragment>
      <div className="App">
        <BrowserRouter>
          <Switch>
            <Route exact path="/" component={Home} />
            <PrivateRoute exact path="/test" component={Test} />
            <Route exact path="/register" component={Signup} />
            <Route exact path="/login" component={Login} />
          </Switch>
          <AuthVerify logOut={logOut} />
        </BrowserRouter>
      </div>
    </Fragment>
  );
}

I suggest converting AuthVerify into a custom hook since it's not rendering content.

const useRouteListener = (cb) => {
  const history = useHistory();

  useEffect(() => {
    return history.listen(cb);
  }, [cb, history]);
}

Move the Router to wrap the App component and call the useRouteListener hook and pass your logout token handler. checkToken here is a callback that would check for the JWT expiration and conditionally invoke your logout procedure.

function App() {
  useRouteListener(checkToken);

  return (
    <Fragment>
      <div className="App">
        <Switch>
          <Route exact path="/" component={Home} />
          <PrivateRoute exact path="/test" component={Test} />
          <Route exact path="/register" component={Signup} />
          <Route exact path="/login" component={Login} />
        </Switch>
      </div>
    </Fragment>
  );
}

Edit invoke-logout-function-when-route-changes

CodePudding user response:

lets make a use of useLocation hook of react-router-dom

//import the useLocation
import { BrowserRouter, Switch, Route,useLocation } from "react-router-dom";

let location = useLocation()

useEffect(()=>{
//do whatever want to perform, just make sure it is in parent component
logOut()
},[location])


const logOut = () => {
  localStorage.removeItem("accessToken");
  localStorage.removeItem("refreshToken");
};

function App() {
  return (
    <Fragment>
      <div className="App">
        <BrowserRouter>
          <Switch>
            <Route exact path="/" component={Home} />
            <PrivateRoute exact path="/test" component={Test} />
            <Route exact path="/register" component={Signup} />
            <Route exact path="/login" component={Login} />
          </Switch>
        </BrowserRouter>
      </div>
      <AuthVerify logOut={logOut} />
    </Fragment>
  );
}
  • Related