I've written a custom ProtectedRoute component for my React app that redirects a user to a /api/login route on my Express API if that user is not authenticated. The /api/login route returns a 301 Redirect to the Auth0 Universal Login UI.
I know the /api/login route on the Express API works because I can hit it directly I get redirected to the Auth0 Universal Login (see the last code snippet).
I also know the ProtectedRoute is redirecting correctly because it redirects to localhost:3000/api/login, which is the correct route on the Express API to trigger the Auth0 Universal Login redirect.
What actually happens though is that localhost:3000/api/login shows up in the address bar but the redirect to the Auth0 Universal Login doesn't happen. That being said if I refresh the page then the redirect to the Universal Login UI works.
I'm not exactly sure why the Redirect returned from /api/login isn't followed in the Browser. I think it has something to do with how React is navigating to the route.
Here's the relevant code snippets. If more are needed let me know.
Protected Route Component
import { Navigate, useLocation } from 'react-router-dom';
interface ISession {
userId: string;
role: string;
details: any;
}
type RouteProps = {
children?: JSX.Element;
session: ISession;
loading: boolean;
};
const ProtectedRoute = ({ session, children, loading }: RouteProps) => {
const location = useLocation();
if (loading) return null;
else if (!!session.userId) {
return children ? children : <Outlet />;
}
else {
return <Navigate replace to="/api/login" state={{ redirectTo: location.pathname }} />;
}
};
export default ProtectedRoute;
How the ProtectedRoute Component is used with React Router
import { useContext } from 'react';
import { Route, Routes } from 'react-router-dom';
import Home from './pages/home';
import 'bootstrap/dist/js/bootstrap.bundle.min';
import { SessionContext } from './context/SessionContext';
import ProtectedRoute from './middleware/protectedRoute';
const App = () => {
const { session, loading } = useContext(SessionContext);
console.log('Session', session);
return (
<Routes>
<Route element={<ProtectedRoute loading={loading} session={session} />}>
<Route path="/" element={<Home />} />
<Route path="/dne" element={ <p>Stuff</p> } />
</Route>
</Routes>
);
};
export default App;
NOTE: I'm excluding the code from the SessionContext component for brevity. Since the user isn't able to login because the redirect to the Auth0 Universal Login UI doesn't work no session is ever created.
The "/api/login" route handler on the Express API
const login = (req: Request, res: Response) => {
const { redirectTo } = req.query;
const domain = config.get('auth0.domain');
const clientId = config.get('auth0.clientId');
const host = config.get('host');
// These 301 Responses are the Redirect to Auth0's Universal Login UI
if (redirectTo) {
const encodedRedirect = base64.urlEncode(redirectTo as string); // A Custom Base64 encoder that is URL Safe
res.status(301).redirect(`${domain}/auth/authorize?response_type=code&scope=openid&client_id=${clientId}&redirect_uri=${host}/api/auth/callback&state=${encodedRedirect}`)
} else {
res.status(301).redirect(`${domain}/auth/authorize?response_type=code&scope=openid&client_id=${clientId}&redirect_uri=${host}/api/auth/callback`);
}
};