1

Say the user is already logged in and redirected to home page again if he modifies the url to navigate to login page it should not do go back to login.

Login.js

const [loginUser, { data, isLoading, isError }] = useLoginUserMutation();
useEffect(() => {
if (data?.response === "true" && data?.state === "success"){
setErrorMsg("");
setEmail("");
setPassword("");
setIsAuthenticated(true);
navigate("/home", { state: { user: data?.user } });
}
else if (data?.response === "false" && data.state === "error"){
  setErrorMsg(true);
}
  else{
    setErrorMsg(false)
  }
}, [data,isError,isLoading]);

const handleLogin = async (e) => {
e.preventDefault();
console.log("****")
await loginUser({email,password})

}

App.js

function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const handleLogin = () => {
setIsAuthenticated(true);
}

return (
<div className="App">
<Router>
<Routes>
  <Route exact path="/">
    <Login onLogin={handleLogin} />
  </Route>
  <ProtectedRoute path="/home" component={Home} isAuthenticated= 
 {isAuthenticated}/>
</Routes>
</Router>
</div>
);
}
export default App;

protectedRoute.js

import React from "react";
import { Navigate,Outlet } from "react-router-dom";

const ProtectedRoute = ( {isAuthenticated}) => {
return isAuthenticated? <Outlet /> : <Navigate to="/" />;
}

export default ProtectedRoute;

UserApi

import{createApi,fetchBaseQuery} from'@reduxjs/toolkit/query/react'

export const authApi = createApi({
reducerPath: 'authApi',
baseQuery: fetchBaseQuery({ baseUrl: "http://localhost:8100" }),
endpoints: (builder) => ({
  loginUser: builder.mutation({
    query: (body) => {
      return {
        url: "users/authenticate",
        method: "post",
        body,
      };
    },
    transformResponse: (response) => {
      const { response: resp, state, user } = response;
      return { response: resp, state: state, user: user };
    },
  }),
}),
});

export const { useLoginUserMutation } = authApi;
enter code here

I tried persisting the state Auth with local storage, since data wont be available it shows undefined/null.

const isAuthenticated = localStorage.getItem('data');

if (isAuthenticated) {
setAuthenticationStatus(true);

since I am using Redux Toolkit I am not sure if I have to use localStorage to persist the data.

Any solution would be appreciated

first render localhost:3000(loginPage) After successfull LoggedIn localhost:3000/home Manually i change the URL: localhost:3000/ it comes back to login page

R9102
  • 687
  • 1
  • 9
  • 32
  • Well in the perfect scenario you'd have a *loading* screen as initial state `true` and you'd be sending a HTTP request at your backend to see if the user is still authorized, and based on the response a *truthy* condition would redirect the user back at *HomePage* and a *falsy* condition would set the *loading* state to `false` and keep & render the *Login* component. – Aleksandar Mar 30 '23 at 20:44
  • And if you'd like to rely on *localStorage* you'd check whether there is a *user-auth* data and based on that condition either redirect to *HomePage* or render *Login* component. – Aleksandar Mar 30 '23 at 20:47
  • @Aleksandar I tried with the localstorage if we add the localstorage.getItem('data') it will trigger directly to home page/home else it says undefined – R9102 Mar 31 '23 at 03:58
  • Have you fixed all your router/routing and protected route issues? I thought we already discussed all this in your other [post](https://stackoverflow.com/q/75890963/8690857) and chat. What part of the auth redirects do you still need help with? Please [edit] to add your routes, route protection components, the *complete* login component, and where/how you are storing the authentication state. – Drew Reese Mar 31 '23 at 04:15
  • @Drew Reese I fixed protected issue, but this is not getting fixed – R9102 Mar 31 '23 at 04:52
  • Added all the code @DrewReese – R9102 Mar 31 '23 at 04:57
  • `App` is still incorrectly rendering `Login` as a child of `Route`, and `ProtectedRoute` is still rendered as a child of `Routes`. Can you also add your redux code where the auth state is stored, or are you still just using local component state? It's not clear what the app's source of truth is for any authentication status. – Drew Reese Mar 31 '23 at 05:20
  • Added my redux code – R9102 Mar 31 '23 at 05:56
  • I see, so you have just the mutation to handle making the POST request to the `"users/authenticate"` endpoint, but you don't store anything in the redux state that says a user is authenticated? So you've got *just* the `isAuthenticated` state in `App` and duplicate `isAuthenticated` state in `Login`? – Drew Reese Mar 31 '23 at 06:05
  • Yes, you are exactly correct Do you suggest storing user authenticated in the store, if yes how do we do in RTK query?Something called tag we need to use? and how do we check it in the component level? – R9102 Mar 31 '23 at 06:13
  • No, you can dispatch an action to the store via the `onQueryStarted` property of a mutation which is passed a "ThunkAPI"-like object, but you'll also need to create a stateSlice to hold it. It's trivial to do with RTK if you've already use `configureStore` in the app root. – Drew Reese Mar 31 '23 at 06:29
  • Any example on that, or you can modify the code,It would be helpful for others as well – R9102 Mar 31 '23 at 06:34

1 Answers1

1

The main issue is that the isAuthenticated state isn't persisted to any longterm storage and ins't initialized from longterm storage when the app mounts.

The easier short-term solution is to use the isAuthenticated state that's declared in App and pass isAuthenticated state and updater function down as props to the routed components that need it.

To combat the issue of "authenticated" users later accessing the "/login" route the answer is to create another route protection component that applies the inverse of the ProtectedRoutes component, e.g. authenticated users are bounced off the route.

Route protectors:

import React from "react";
import { Navigate, Outlet } from "react-router-dom";

// Unauthenticated users redirected to log in route
const ProtectedRoute = ({ isAuthenticated }) => {
  return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />;
};

// Authenticated users redirected to safe route
const AnonymousRoute = ({ isAuthenticated }) => {
  return isAuthenticated ? <Navigate to="/" replace /> : <Outlet />;
};
function App() {
  // Initialize state from localStorage
  const [isAuthenticated, setIsAuthenticated] = useState(() => {
    return JSON.parse(localStorage.getItem("auth")) ?? false;
  });

  // Side-effect to persist state changes to localStorage
  useEffect(() => {
    localStorage.setItem("auth", JSON.stringify(isAuthenticated));
  }, [isAuthenticated]);

  const handleLogin = () => {
    setIsAuthenticated(true);
  };

  return (
    <div className="App">
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Navigate to="/home" replace />} />

          <Route element={<AnonymousRoute isAuthenticated={isAuthenticated} />}>
            <Route path="/login" element={<Login onLogin={handleLogin} />} />
            {/* ... other "anonymous" routes ... */}
          </Route>

          <Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
            <Route path="/home" element={<Home />} />
            {/* ... other "authenticated" routes ... */}
          </Route>
        </Routes>
      </BrowserRouter>
    </div>
  );
}
export default App;
const Login = ({ onLogin }) => {
  const navigate = useNavigate();
  const [loginUser, { data, isLoading, isError }] = useLoginUserMutation();
  ...

  const handleLogin = async (e) => {
    e.preventDefault();

    setErrorMsg("");
    try {
      // Call mutation trigger and await and unwrap resolved response
      await loginUser({ email, password }).unwrap();

      // Update the auth state
      onLogin();

      // Redirect back to home
      navigate("/home", { replace: true });
    } catch(error) {
      setErrorMsg(true);
    }
  };

  ...
};
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • const AnonymousRoute = ({ isAuthenticated }) => { return isAuthenticated ? : ; }; AnonymousRoute Means? any other route which need not authentications right – R9102 Mar 31 '23 at 07:19
  • 1
    @R9102 These are the routes you want authenticated users ***not*** to access. The example being from your example, a user just logged in successfully, then tries to navigate to `"/login"` either via link or manual entry in the address bar, and since they are already authenticated they are bounced off the route. – Drew Reese Mar 31 '23 at 07:21
  • with this code on inital render it directly goes to home page – R9102 Mar 31 '23 at 07:32
  • @R9102 If they are authenticated, yes. Are you seeing unauthenticated user able to access `"/home"`? – Drew Reese Mar 31 '23 at 07:33
  • Ok probably My code Login is "/" and home page is "/home" Probably you have to change it – R9102 Mar 31 '23 at 07:35
  • @R9102 If you recall I did suggest (*probably not strongly enough*) to ***not*** have the app entry point/base route `"/"` ***be*** the login route. Things are easier to manage when you have a separate dedicated authentication route. If you really want `"/"` to be the login route though you can do that, and the logic should all still work about the same. – Drew Reese Mar 31 '23 at 07:38
  • Now How do i get the userdata to other pages? from the loginpage and pass it to home page – R9102 Mar 31 '23 at 07:50
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/252889/discussion-between-drew-reese-and-r9102). – Drew Reese Mar 31 '23 at 07:51