'NextJS / React - how to avoid multiple requests to server?

I'm writing a little side project in which I have a question about how to design my frontend authorization flow.

Stack is: Next.js / Express / MDB

Here's the issue with the authorization: In my app there are pages that should be only served to instructors. When the app mounts I have a call to my backend checking for a stored cookie and authorize the user. The returned user is stored in a Context.

authContext.js

useEffect(() => {
    checkUserLoggedIn();
  }, []);

...

const checkUserLoggedIn = async () => {
    const res = await fetch(`${process.env.NEXT_PUBLIC_CLIENT}/user`);
    const data = await res.json();

    if (res.status === 200) {
      setUser(data.user);
    } else {
      setUser(null);
    }
  };

That works perfectly fine. But now comes the part where I struggle. The protected pages are wrapped by a component that checks again if the user is a) authenticated and b) authorized.

wrapInstructor.js

const CheckInstructor = ({ token, children }) => {
  const [ok, setOk] = useState(false);
  const [login, setLogin] = useState(null);

 useEffect(async () => {
    try {
      const user = await fetch(
        `${process.env.NEXT_PUBLIC_APIURL}/me`,
        {
          method: "GET",
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      );
      const { data, error } = await user.json();

      if (error) {
        toast.error("Auf diese Seite können nur eingeloggte User zugreifen");
        setLogin(false);
      }
      if (data && data.role.includes("INSTRUCTOR")) {
        setOk(true);
        setLogin(true);
      }
    } catch (error) {
      toast.error(error);
      setLogin(false);
    }
  }, []);

  return !ok ? (
    <>{login === false ? <NotLoggedIn /> : <Spinner />}</>
  ) : (
    <>{children}</>
  );
};

export default CheckInstructor;

Now here comes the problem: When a user mounts the app on a path that is protected the app fires off two authentication requests simultaniously.

The first one gets a valid response (the request from context). The second one from the wrapper gets a 304 status code which indicates that the response has not changed since the last request.

But since the wrapper expects a valid response the content of the protected pages will not show.

How can I cope with this problem? (NOTE: I'm in development mode, tested from localhost)

My ideas were:

  1. The wrapper component does not make another call to the server - the user in the context is the single source of truth and the wrapper only checks the already stored user --> Question: Is this a safe practice? Can the store (React Context or Redux) be manipulated by the user which would make my app unsafe?

  2. In the wrapper component also show the children components if the status code is 304 AND a user is already stored in the context

  3. Write another endpoint in the backend so both requests are sent to different routes (I would concider this bad practice because DRY)

I'm a bit lost here - can you help me to clear my thoughts?

Thanks a lot!



Solution 1:[1]

There's no need to visit the same endpoint twice to check if a user is authorized to visit a certain page. What you store in auth context would be enough. Just make sure to update the value whenever a role or permissions change.

Regarding your security concern, you shouldn't consider any client app safe. Even if you add an actual endpoint call on every protected page, there's still a way to call the endpoints directly(curl, postman, you name it).

The problem should be solved by introducing authorization checks on every protected API route. This way you would never worry about corrupted clients at all.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alex Khismatulin