1
const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
  {
    path: "/main",
    element: (
      <AdminAuth redirectTo="/profile">
        <Main />
      </AdminAuth>
    ),
  },
])
import React from "react";

import { Link, Navigate } from "react-router-dom";
import { isAutheticated } from "../auth";

export const AdminAuth = ({ children, redirectTo }) => {
  let auth = isAutheticated().user_Role;
  return auth === "admin" ? children : <Navigate to={redirectTo} />;
};

I want to prevent routing while user manually changes url in the browser. I have the "/main" as an admin route, which I'm protecting, but the issue starts when user changes his role in the local storage and tries to access `"/main". I want to prevent user from manually changing the route in their url or show error if they change manually.

EDIT: I'm protecting my route in the backend, but in the frontend I don't user to even access this.

  • 1
    It seems like the title of your question and the body are conflicting; am I wrong? – Youssouf Oumar Feb 01 '23 at 10:41
  • Are you just asking how to create [protected routes](https://stackoverflow.com/questions/66289122/how-to-create-a-protected-route)? Otherwise, I agree with yousoumar in that your post's title and body are not in agreement with what you say is an issue and what you want resolved. Can you [edit] and clarify the issue? – Drew Reese Feb 01 '23 at 17:36
  • @yousoumar yes done. –  Feb 02 '23 at 07:07

4 Answers4

0

Considering that all code on the client can be changed by the user, setting up protected routes like yours, should only be for a better user experience. You cannot and should not depend on client logic to protect data from non-admin users. This has to be done on the backend/server.

Therefore, using local storage as your business logic for identifying an admin user or not, would be highly critical, since everyone could change that. Instead it should be done by some token, for exmaple JWT (Json Web Token) using a authentication provider like AWS Cognito or similiar, or even building your own server side authetication logic.

Gustav Vingtoft
  • 355
  • 2
  • 16
  • i'm protecting it from backend but just make sure if someone does it then don't them even see the UI. @gustav-vingtoft –  Feb 01 '23 at 10:33
  • Then you are good. Because this is clientisde, you cannot ensure 100% that users will bypass your routing logic if they alter their localStorage and/or code files in the browser. Adding routing logic should only be for user experience. – Gustav Vingtoft Feb 01 '23 at 11:15
0

Simply by sending prop from previous page, you can control and simply send the user back to the proper page.

0

I want to prevent user from manually changing the route in their url or show error if they change manually.

There's simply just no way of knowing from inside the app how exactly the URL changed in the address bar. When a user does this the browser completely reloads the page, so the entire React app is mounted fresh. react-router can then only read what the current URL is to handle route matching and rendering.

If you are certain that you don't trust the frontend client then a solution here would be to validate the user against the backend on every route/component you want to protect each time they are navigated to.

Example implementation:

import React from "react";

import { Navigate } from "react-router-dom";
import { authService } from "../auth";

export const AdminAuth = ({ children, redirectTo, role }) => {
  const [userRole, setUserRole] = React.useState();

  React.useEffect(() => {
    authService.validateUser()
      .then(user => {
        setUserRole(user.user_Role);
      });
  }, []);

  if (userRole === undefined) {
    return null; // or loading indicator/spinner/etc
  }

  return userRole === role
    ? children
    : <Navigate to={redirectTo} replace />;
};
const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
  {
    path: "/main",
    element: (
      <AdminAuth redirectTo="/profile" role="admin">
        <Main />
      </AdminAuth>
    ),
  },
]);

Note that checking with the backend for each and every route transition will incur a network cost and slow the frontend UI page transitions down. Depending on the sensitivity of the content this may be an acceptable cost. That's a decision only you or your product owners can make.

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • So you are telling me that it is absolutely impossible to secure frontend without checking each time from backend –  Feb 02 '23 at 07:06
  • @Gkchat More or less, yes. The app code is sent to the client, running in their browser, so they can inspect and tweak it. This is why you protect the backend APIs for data that is requested. If adversarial users can't actually get access to backend APIs then generally it's not considered harmful if they bypass some route protection and see a possibly empty page because they didn't get any data to display. This is if they didn't receive an error trying to load page content that is possibly missing data. – Drew Reese Feb 02 '23 at 07:11
  • @Gkchat The route protections aren't there to protect data, but to guide users to authenticate so when redirected back to protected pages they can correctly access backend services you are providing. It's more UI/UX than it is actual data security like Gustav puts it in their answer. – Drew Reese Feb 02 '23 at 07:17
0

you can make a state variable (let's say an ID) in the top-level component by using Maths.random() and place that number before the route's end word so when each time page renders, the URL is random except the last word of the URL. The pro is that when the user looks at that URL and memorizes it and then tries to access that page using that URL will not work because this time page URL is different since the Maths.random() will be called again as a result of the re-rendering of the top level App component.

let's say the previous URL is http://localhost:3000/todo by putting a number (id) before the route's last word we get, http://localhost:3000/{id}/todo id is some random number.What happens is let's say when the first time the page loads the id is equal to 10023. And let's say the user remembers that number and put that in the address tab. So then the page reloads and maths.random() will be called and the new id to that page will be changed. So as a result let's say the new id is equal to 1000. The trick is the user only knows the URL of the previous version of the same page. But there is a problem with this approach. Even though it prevents the problem of the user knowing the correct URL to the page when the user refreshes the page, the content of the page will be gone also.