How to protect routes with preact-router?

397 views Asked by At

I'm able to use "preact-router" on a component level together with a authContext.

Eg:

export const CartPage = (): JSX.Element => {
  const { isAuthenticated } = useAuthContext();

  return (
    <>
      {isAuthenticated ? (
        <Cart />
      ) : (
        (() => {
          route("/login");
        })()
      )}
    </>
  );
};

This works great when using the app as intended by navigating via the apps navigation, views etc. But if I try to navigate by using the browsers address bar to hit a protected route eg: "/cart" the redirect via route() to login is not triggered, instead I just get a blank page...

app.tsx:

export function App() {
  return (
    <div>
      <AuthProvider>
        <Navbar />

        <Layout>
          <ViewToggleProvider>
            <Router>
              <Route path="/" component={HomePage} />

              <AsyncRoute path="/login" component={LoginPage} />

              <AsyncRoute exact path="/cart" component={CartPage} />

              <AsyncRoute
                path="/orders/:orderId"
                component={OrderDetailsPage}
              />
            </Router>
          </ViewToggleProvider>
        </Layout>
      </AuthProvider>
    </div>
  );
}

Perhaps I need to protect the routes on a "route" level over a "component" level?

Glad for any assistance here.

2

There are 2 answers

0
rschristian On BEST ANSWER

Easiest way would be to make a ProtectedRoute component, rather than handling that in each component:

function ProtectedRoute(props) {
    const { isAuthenticated } = useAuthContext();

    useEffect(() => {
        if (!isAuthenticated) route('/login', true);
    }, [isAuthenticated]);

    return <Route {...props} />;
}

Then to use it:

<Router>
  <ProtectedRoute exact path="/cart" component={CartPage} />
</Router>

You need to use useEffect (or similar) to ensure it's triggered on browser navigation.

0
sameraze agvvl On

to enhance the security of the ProtectedRoute you have to consider several factors like

  • server-side
  • authentication
  • route-level permissions
  • token expiration

it is an example of updated code

 import { Route, Redirect } from 'react-router-dom';
 import { useAuthContext } from 'path/to/auth-context';

  function ProtectedRoute({ 
     component: Component, requiredPermissions,
        ...rest }) {
    const { isAuthenticated, userPermissions } = useAuthContext();

    const isAuthorized = () => {
    // Check if user is authenticated
    if (!isAuthenticated) {
       return false;
     }

    // Check if user has the required permissions for the route
    if (requiredPermissions && requiredPermissions.length > 0) {
         return requiredPermissions.every(permission =>
        userPermissions.includes(permission)
       );
       }

     // No specific permissions required, allow access
     return true;
     };

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

    export default ProtectedRoute;