T
TanStack•2y ago
fair-rose

Best way to handle authenticated routes with http-only cookie

I am currently trying to find a way to process a http-only session cookie with Tanstack Router. The docs explain that you can use the beforeLoad-fn to redirect to the login route if necessary. My routes are /login /app (here starts the auth-area) /app/vouchers /app/...
export const Route = new FileRoute("/app").createRoute({
beforeLoad: async ({ context: { queryClient } }) => {
try {
const user = await queryClient.ensureQueryData(currentUserQueryOptions());
return { user };
} catch (error) {
console.log(error);
}
},
component: App,
});
export const Route = new FileRoute("/app").createRoute({
beforeLoad: async ({ context: { queryClient } }) => {
try {
const user = await queryClient.ensureQueryData(currentUserQueryOptions());
return { user };
} catch (error) {
console.log(error);
}
},
component: App,
});
However, as an http-only cookie cannot be read on the client side, this option is difficult to implement. Therefore I have a zustand-store, which keeps the isLoggedIn-prop. In case of an 401 error the prop is set to false.
export const queryClient = new QueryClient({
//....
queryCache: new QueryCache({
onError: (error) => {
if (error?.response?.status === 401) {
getToggleIsLoggedInAction()(false);
}
},
}),
});
export const queryClient = new QueryClient({
//....
queryCache: new QueryCache({
onError: (error) => {
if (error?.response?.status === 401) {
getToggleIsLoggedInAction()(false);
}
},
}),
});
So far, I haven't come up with anything other than handling this prop in the app component with the help of useEffect
function App() {//component of Route /app
const isLoggedIn = useIsLoggedIn();
const { logout } = useLoginActions();
const location = useRouterState({ select: (s) => s.location });
const user = Route.useRouteContext({
select: (s) => s.user,
});
const navigate = Route.useNavigate();

useEffect(() => {
if (!isLoggedIn && !location.href.includes("login")) {//feels a bit odd
navigate({
to: "/login",
search: { redirect: location.href },
});
}
}, [isLoggedIn, navigate, location.href]);

const logoutUser = () => {
logout({ app: "vouchers" });
};

return (
<div>
<NavBar user={user} onLogout={logoutUser} />
<div>
<Outlet />
</div>
</div>
);
}
function App() {//component of Route /app
const isLoggedIn = useIsLoggedIn();
const { logout } = useLoginActions();
const location = useRouterState({ select: (s) => s.location });
const user = Route.useRouteContext({
select: (s) => s.user,
});
const navigate = Route.useNavigate();

useEffect(() => {
if (!isLoggedIn && !location.href.includes("login")) {//feels a bit odd
navigate({
to: "/login",
search: { redirect: location.href },
});
}
}, [isLoggedIn, navigate, location.href]);

const logoutUser = () => {
logout({ app: "vouchers" });
};

return (
<div>
<NavBar user={user} onLogout={logoutUser} />
<div>
<Outlet />
</div>
</div>
);
}
I would be very happy to receive feedback on how you would solve this. Thx!
6 Replies
rising-crimson
rising-crimson•2y ago
I do this by putting all my auth-required routes under a shared layout route (which you don't really even need to return a component for). https://github.com/SeanCassiere/nv-rental-clone/blob/master/src/routes/_auth.tsx Then, in that auth layout route, I check the user's auth status, and throw a redirect.
GitHub
nv-rental-clone/src/routes/_auth.tsx at master · SeanCassiere/nv-re...
Navotar with Tailwind and the Tanstack. Contribute to SeanCassiere/nv-rental-clone development by creating an account on GitHub.
rising-crimson
rising-crimson•2y ago
None of my auth related stuff, living in the React components, pretty much all of it is a part of my beforeLoad logic in my auth layout route. You could set it up instead of using a layout route, but rather setting at the /app route.
src/routes/
index.tsx
app/
route <- put your auth required logic here
foo.tsx <- to get to this page, it'll need to be auth required because of the above logic
src/routes/
index.tsx
app/
route <- put your auth required logic here
foo.tsx <- to get to this page, it'll need to be auth required because of the above logic
We don't prescribe a specific way to doing auth, since everyone's way of handling auth is just a different from the next person. For example, my set up above uses context and a useAuth hook. Yours may be different.
extended-salmon
extended-salmon•2y ago
Something like this would be good to include in the docs somehow. A couple of things clicked by watching this repo. Perhaps I'm just too eager to get going and won't sit down and read properly (although I kinda did), but yeah I missed some stuff.
fair-rose
fair-roseOP•2y ago
@Sean Cassiere Thank you! I already have my auth-checks in the "app/" route, similar to you.
rising-crimson
rising-crimson•2y ago
That's understandable, but the sheer number of permutations in the way people handle auth is just too much to cover in the docs. The building blocks are there. The router has context, it has parent routes, and layout routes. It has the beforeLoad and loader callbacks. For there on, it a matter of making it work for you. And this is just for normal username-password auth. Oauth has its own complications, and the libraries people use for it.
extended-salmon
extended-salmon•2y ago
Yes, absolutely, that's understandable. Thanks for answering and chatting Sean! You're doing great work here in the chat! 🎊

Did you find this page helpful?