0

I've came across a problem with protected routes and the authentication of protected routes.

I'm using an AuthContext for the whole user authentication across the webapp. I save all the user-data inside a state. With the help of useEffect and the sessionStorage I store the user object so It can be used after a page reload.

[...]
export const AuthProvider = ({ children }) => {
    const [user, setUser] = useState({})

    useEffect(() => {
        const sessionUser = sessionStorage.getItem("user")
        if(!sessionUser) return
        changeUser(JSON.parse(sessionUser))
    }, [])

    const hasRank = (ranks) => {
        if(!Object.keys(user).length > 0) return false
        const matchingPerms = ranks.filter(rank => user.rank.includes(rank))
        return matchingPerms.length > 0
    }

    const changeUser = (data) => {
        setUser(data)
        if(Object.keys(data).length > 0) {
            return sessionStorage.setItem("user", JSON.stringify(data))
        }
        return sessionStorage.removeItem("user")
    }
}
[...]

In order to protect certain pages I'm using a Protected Route Component that checks whether the user is logged in or not.

[...]
const auth = useContext(AuthContext)
const isAuthorized = auth.hasRank(rest.rank)

<Route
    {...rest}
    render={props => {
        return isAuthorized ? (
            <Component {...props} />
        ) : (
            <Redirect to="/auth/login" />
        )
    }}
/>

The saving and fetching into and from the sessionStorage works fine until I want to render content that's inside a protected route - I always get redirected to the login page, because the user object is empty because of the reload and the state is not being updated early enough. Therefore the protected route checks for authentication with an empty object, which results in a redirect to the login page.

How can I wait until the user state is being updated before checking the authentication inside the protected route?

EDIT:

App Component:

return (
        <Router>
            <Switch>
                <Route path="/auth/register" component={Register} />
                <Route path="/auth/login" component={LogIn} />
                <Route path="/err/404" component={Page404} />
                <Route path="/" component={PanelRoutes}/>
            </Switch>
        </Router>
    )

PanelRoutes Component:

return (
        <div className="skin-default fixed-layout">
            <div id="main-wrapper">
                <Topbar />
                <Sidebar />
                <div className="page-wrapper">
                    <div className="container-fluid">
                        <Switch>
                            <Route exact path="/" component={Homepage} />
                            <Route exact path="/legal/imprint" component={Imprint} />
                            <Route exact path="/legal/privacy" component={Privacy} />
                            <ProtectedRoute exact path="/mc/sounds" component={McSounds} rank={["ADMIN", "MC"]} />
                            <ProtectedRoute exact path="/admin/users" component={AdminUsers} rank={["ADMIN"]} />
                        </Switch>
                    </div>
                </div>
            </div>
        </div>
    )

Kind Regards

Andreas
  • 57
  • 5
  • 1
    i need to see your or root component. Because we always assume whatever you get from the root, when it comes to the auth, should pass to children via a Context. You routes supposed to be only the consumer of this check. When you refresh, you still go through root component again. – windmaomao Aug 01 '21 at 15:26
  • I've edited my initial question and added my app component where the routes are "stored" – Andreas Aug 01 '21 at 15:39
  • Thanks. can i ask where the `AuthProvider` is used? Either your `App` or `PanelRoutes` need to consume it? or there's an upper root component consuming that, can i see that code as well thanks ? Also for the first two code section, what are their component names ? – windmaomao Aug 01 '21 at 17:31
  • The first two component names are: AuthContext and ProtectedRoute. The App or PanelRoutes component itself dont't consume it but Components inside the PanelRoutes consume the AuthContext. – Andreas Aug 02 '21 at 16:59
  • Could it be because you call the asynchronous `setUser` in the `useEffect`? Perhaps it helps to take a look at a similar [problem](https://stackoverflow.com/questions/72163825/adding-oidc-to-an-react-application-with-restricted-routes) I had, where I set the default state of the user based on what is saved in the storage. – Kikkomann Dec 28 '22 at 15:12

0 Answers0