T
TanStack•4y ago
continuing-cyan

Authentication & Authorization in Routes

Any example of good practices to deal with authentication routes? Option 1: Wrap the element with a Auth component which handles the authentication on the client side. e.g fetch the user and if it return erros redirect to login. Option 2: Create a parent route that has an action or loader which tries to fetch the user before it's loaded and do the redirect there? Not to sure if we have any example or recomendation on which option to use but would be cool to use the same concept of publicProcedures, privateProdecures that trpc has to compose new procedures and abstract the logic of authentication and authorization? Was thinking on how it would look like on tanstack/router. I am digging into that now and will update this thread.
15 Replies
fascinating-indigo
fascinating-indigo•4y ago
Looks like the example handles authentication in a layout: https://tanstack.com/router/v1/docs/examples/react/kitchen-sink
React Virtual Kitchen Sink Example | TanStack Virtual Docs
An example showing how to implement Kitchen Sink in React Virtual
fascinating-indigo
fascinating-indigo•4y ago
We're working on route middleware that will likely fit the bill here. What kind of API pattern would you like to see here?
continuing-cyan
continuing-cyanOP•4y ago
I was thinking in a concept of a guard or an async function that allow us to do a async call and return true or false or redirect the if the user hasn't permission to access the view, therefore most of the frontend authentication use hooks, like react query, so I think we should be able to also use hooks and not only an async js function. @Tanner Linsley to be honest I think your router is so special and definitely solve a problem that NextJS and other frameworks didn't solve. When I saw it the first time (react location) it remembered me of Angular router (ofc this one is much more complete with type safety, etc), but I think angular has a good example of a simple and useful API to handle that: https://angular.io/api/router/CanActivate But if we have route middleware that should be fine to do using that. I also tried to implement that using a route ID wrapper.
No description
deep-jade
deep-jade•4y ago
Hi, I'm trying out tanstack router in my application, and looking to find a solution to this as well. This is the pattern I've used in react router up to now.
import { Redirect, useLocation } from 'react-router-dom';
import { NotFound } from '@screens/404/NotFound';

type Props = {
children: React.ReactNode;
isPermitted: () => boolean;
redirectTo?: string | (() => string);
};

/**
* A component that can be used inside a Route to run an 'isPermitted' callback, and if that returns false either:
*
* 1) show a 404 page
* 2) if a `redirectTo` is provided, redirect to that path.
*/
export function Protected({ children, isPermitted, redirectTo }: Props) {
const location = useLocation();

return isPermitted() ? (
<>{children}</>
) : redirectTo ? (
<Redirect
to={{
pathname: typeof redirectTo === 'function' ? redirectTo() : redirectTo,
state: { from: location },
}}
/>
) : (
<NotFound />
);
}
import { Redirect, useLocation } from 'react-router-dom';
import { NotFound } from '@screens/404/NotFound';

type Props = {
children: React.ReactNode;
isPermitted: () => boolean;
redirectTo?: string | (() => string);
};

/**
* A component that can be used inside a Route to run an 'isPermitted' callback, and if that returns false either:
*
* 1) show a 404 page
* 2) if a `redirectTo` is provided, redirect to that path.
*/
export function Protected({ children, isPermitted, redirectTo }: Props) {
const location = useLocation();

return isPermitted() ? (
<>{children}</>
) : redirectTo ? (
<Redirect
to={{
pathname: typeof redirectTo === 'function' ? redirectTo() : redirectTo,
state: { from: location },
}}
/>
) : (
<NotFound />
);
}
For tanstack router, I could imagine replacing children with Outlet, but I'm not sure how to redirect, since to get the router I think I need to provide a route string, but this helper could be used anywhere. (I'm very early in figuring out tanstack router, piecing it together from the examples). I see mention of a Navigation component in one of the examples, but I'm not finding it so far...
fascinating-indigo
fascinating-indigo•4y ago
The middleware routing is almost ready. Which should take care of this challenge
deep-jade
deep-jade•4y ago
Great I'll hold off until that's ready, thanks! I'm excited to dig in and play around with this some more. Really looking forward to typesafe routing. 🙂
fascinating-indigo
fascinating-indigo•4y ago
const authenticatedRoute = rootRoute.createRoute({
path: 'authenticated',
component: Authenticated,
beforeLoad: ({ context }) => {
if (context.auth.status === 'loggedOut') {
throw router.navigate({
to: loginRoute.id,
search: {
redirect: router.location.href,
},
})
}
},
})
const authenticatedRoute = rootRoute.createRoute({
path: 'authenticated',
component: Authenticated,
beforeLoad: ({ context }) => {
if (context.auth.status === 'loggedOut') {
throw router.navigate({
to: loginRoute.id,
search: {
redirect: router.location.href,
},
})
}
},
})
const router = createReactRouter({
routeConfig,
useContext: () => {
return {
auth: React.useContext(AuthContext),
}
},
})
const router = createReactRouter({
routeConfig,
useContext: () => {
return {
auth: React.useContext(AuthContext),
}
},
})
continuing-cyan
continuing-cyanOP•4y ago
That's great @Tanner Linsley ! Great addition! thank you
fair-rose
fair-rose•4y ago
Hi @Tanner Linsley tried using like this but after redirecting to login page it does not render the component, refresing the page manaully renders the page?
flat-fuchsia
flat-fuchsia•3y ago
Is there any way that the beforeLoad function could provide the router? It seems circular that router depends on routeConfig, which depnds on routes to be registered via addChildren, and some of these routes will depend on the router to do authorization. I know this is a small aesthetic gripe, but it prevents you from breaking up your router into separate files.
fascinating-indigo
fascinating-indigo•3y ago
I’ll look into it
flat-fuchsia
flat-fuchsia•3y ago
Also, the RouterContext type doesn't seem to be propagating through to my routes. I am using preact. The value does go through at runtime though. Let me know if there is any testing I can do / if I should collate this into a formal issue.
No description
No description
fascinating-indigo
fascinating-indigo•3y ago
This is changing I think. We’ll see It’ll get better
flat-fuchsia
flat-fuchsia•3y ago
No worries! I know it’s beta software, and it’s already really great. I’m using it for my new startup, let me know if there is any feedback I can give to help It seems like the correct way to do authorization is within the routes component (using an AuthContext), judging by the kitchen sink example (and judging by context being removed from beforeLoad). Is this how we should be approaching this now?
fascinating-indigo
fascinating-indigo•3y ago
Pretty sure. It's tricky because you can technically handle auth in a few different ways. For instance, you could just add a provider at the top of your app and manage the auth state there. At the end of the day, I just wanted to make sure that all of the different patterns would work Including SSR authentication, so that on a server-side render of the router could detect non-authed usage, redirect and serve the right content to the user. Essentially: if you want to handle auth on the client, you can use effects and hooks like most SPAs use. If you want to support auth on the server, you should use the beforeLoad and onLoadError combo with a redirect Either approach will work for the client

Did you find this page helpful?