T
TanStack•2y ago
equal-jade

using authenticatedRoute and context

I have this authenticated route that should inject userID into the context i can access this object in my sub-routes but user object is User | Undefined I'd like to mark a subset of file routes as authenticated, so they will have full User
export const _authenticated = createFileRoute("/_authenticated")({
beforeLoad: async ({ context, location }) => {
const session = await context.supabase.auth.getSession();
if (!session.data.session?.access_token) {
throw redirect({
to: login.to,
search: {
redirect: location.href,
},
});
}
setToken(session?.data.session.access_token);
return {
session: session.data.session,
};
},
component: () => {
const stores = trpcClient.stores.getStores.useQuery();
const safes = trpcClient.stores.getSafes.useQuery();

useEffect(() => {

console.log("authRoutestores 1", {as: stores.data, sasd: safes.data});
// if stores and safes are not yet created, redirect to onboarding
if (stores.data?.length === 0 && safes.data?.length === 0) {
console.log("authRoutestores 2", {as: stores.data, sasd: safes.data});
redirect({
to: index.to
});
}
}, [stores.data, safes.data]);

useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log("supabase-event", event);
if (session?.access_token) setToken(session?.access_token);
},
);
return () => {
authListener?.subscription.unsubscribe();
};
});
return <Layout />;
},
});
export const _authenticated = createFileRoute("/_authenticated")({
beforeLoad: async ({ context, location }) => {
const session = await context.supabase.auth.getSession();
if (!session.data.session?.access_token) {
throw redirect({
to: login.to,
search: {
redirect: location.href,
},
});
}
setToken(session?.data.session.access_token);
return {
session: session.data.session,
};
},
component: () => {
const stores = trpcClient.stores.getStores.useQuery();
const safes = trpcClient.stores.getSafes.useQuery();

useEffect(() => {

console.log("authRoutestores 1", {as: stores.data, sasd: safes.data});
// if stores and safes are not yet created, redirect to onboarding
if (stores.data?.length === 0 && safes.data?.length === 0) {
console.log("authRoutestores 2", {as: stores.data, sasd: safes.data});
redirect({
to: index.to
});
}
}, [stores.data, safes.data]);

useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log("supabase-event", event);
if (session?.access_token) setToken(session?.access_token);
},
);
return () => {
authListener?.subscription.unsubscribe();
};
});
return <Layout />;
},
});
additionaly I'd also like to move to beforeLoad, but I am not sure how to do with trpc
const stores = trpcClient.stores.getStores.useQuery();
const safes = trpcClient.stores.getSafes.useQuery();
const stores = trpcClient.stores.getStores.useQuery();
const safes = trpcClient.stores.getSafes.useQuery();
No description
16 Replies
equal-jade
equal-jadeOP•2y ago
i suppose i can do like this but i'd like to have more assurance that i am using this correctly, feel weird
equal-jade
equal-jadeOP•2y ago
No description
equal-jade
equal-jadeOP•2y ago
ideally this should also be without undefined
const userId1 = getRouteApi("/dashboard").useRouteContext().session;
const userId1 = getRouteApi("/dashboard").useRouteContext().session;
because dashboard should be somehow under the _authenticated layout
equal-jade
equal-jadeOP•2y ago
React Router Authenticated Routes Context Example | TanStack Router...
An example showing how to implement Authenticated Routes Context in React Router
extended-salmon
extended-salmon•2y ago
If you are checking for the User presence in the beforeLoad callback (throwing if not present) and returning, then by all accouts that user should be present in your child routes of the _layout.
equal-jade
equal-jadeOP•2y ago
i really like tanstack patterns, so if we can do without context and with my appraoch that woudl be amazing
export const Route = createFileRoute("/dashboard")({
validateSearch: z.object({
view: ViewOption.optional(),
}),
component: DashboardPage,
});
export const Route = createFileRoute("/dashboard")({
validateSearch: z.object({
view: ViewOption.optional(),
}),
component: DashboardPage,
});
how i mark this as child of _authenticated?
extended-salmon
extended-salmon•2y ago
Could you confirm you are using file-based routing?
equal-jade
equal-jadeOP•2y ago
yes
import { setToken, supabase } from "@/features/TrpcProvider.tsx";
import { Layout } from "@/layout.tsx";
import {redirect, createFileRoute} from "@tanstack/react-router";
import { useEffect } from "react";
import {trpcClient} from "@/features/trpc-client.ts";

export const Route = createFileRoute("/_authenticated")({
beforeLoad: async ({ context, location }) => {
const session = await context.supabase.auth.getSession();
if (!session.data.session?.access_token) {
throw redirect({
to: Route.to,
search: {
redirect: location.href,
},
});
}
setToken(session?.data.session.access_token);
return {
session: session.data.session,
};
},
component: () => {
const stores = trpcClient.stores.getStores.useQuery();
const safes = trpcClient.stores.getSafes.useQuery();

useEffect(() => {

console.log("authRoutestores 1", {as: stores.data, sasd: safes.data});
// if stores and safes are not yet created, redirect to onboarding
if (stores.data?.length === 0 && safes.data?.length === 0) {
console.log("authRoutestores 2", {as: stores.data, sasd: safes.data});
redirect({
to: Route.to
});
}
}, [stores.data, safes.data]);

useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log("supabase-event", event);
if (session?.access_token) setToken(session?.access_token);
},
);
return () => {
authListener?.subscription.unsubscribe();
};
});
return <Layout />;
},
});
import { setToken, supabase } from "@/features/TrpcProvider.tsx";
import { Layout } from "@/layout.tsx";
import {redirect, createFileRoute} from "@tanstack/react-router";
import { useEffect } from "react";
import {trpcClient} from "@/features/trpc-client.ts";

export const Route = createFileRoute("/_authenticated")({
beforeLoad: async ({ context, location }) => {
const session = await context.supabase.auth.getSession();
if (!session.data.session?.access_token) {
throw redirect({
to: Route.to,
search: {
redirect: location.href,
},
});
}
setToken(session?.data.session.access_token);
return {
session: session.data.session,
};
},
component: () => {
const stores = trpcClient.stores.getStores.useQuery();
const safes = trpcClient.stores.getSafes.useQuery();

useEffect(() => {

console.log("authRoutestores 1", {as: stores.data, sasd: safes.data});
// if stores and safes are not yet created, redirect to onboarding
if (stores.data?.length === 0 && safes.data?.length === 0) {
console.log("authRoutestores 2", {as: stores.data, sasd: safes.data});
redirect({
to: Route.to
});
}
}, [stores.data, safes.data]);

useEffect(() => {
const { data: authListener } = supabase.auth.onAuthStateChange(
(event, session) => {
console.log("supabase-event", event);
if (session?.access_token) setToken(session?.access_token);
},
);
return () => {
authListener?.subscription.unsubscribe();
};
});
return <Layout />;
},
});
_authenticated.tsx
import { DashboardPage } from "@/routes/-dashboard/Dashboard.tsx";
import {createFileRoute} from "@tanstack/react-router";
import { z } from "zod";

const viewOptions = [
"Overview",
"Stores",
"Applications",
"Notifications",
] as const;
export const ViewOption = z.enum(viewOptions);
export type ViewOption = (typeof viewOptions)[number];

export const Route = createFileRoute("/dashboard")({
validateSearch: z.object({
view: ViewOption.optional(),
}),
component: DashboardPage,
});
import { DashboardPage } from "@/routes/-dashboard/Dashboard.tsx";
import {createFileRoute} from "@tanstack/react-router";
import { z } from "zod";

const viewOptions = [
"Overview",
"Stores",
"Applications",
"Notifications",
] as const;
export const ViewOption = z.enum(viewOptions);
export type ViewOption = (typeof viewOptions)[number];

export const Route = createFileRoute("/dashboard")({
validateSearch: z.object({
view: ViewOption.optional(),
}),
component: DashboardPage,
});
dashboard.tsx
extended-salmon
extended-salmon•2y ago
Then this should work.
routes/
_authenticated.tsx
_authenticated/
dashboard.route.tsx
routes/
_authenticated.tsx
_authenticated/
dashboard.route.tsx
equal-jade
equal-jadeOP•2y ago
😅 nice one
extended-salmon
extended-salmon•2y ago
If you don't want to use the beforeLoad callback for checking auth, then you could also do this checking in the component lifecycle of the _authenticated layout route's component. However, at this point you'll be assuming control of this stuff since this'll then be pushed out of Router land into the realm of React.
equal-jade
equal-jadeOP•2y ago
i prefer to put as much logic into router lifecycle as possiblle i think it makes code really clean to consume downstream
equal-jade
equal-jadeOP•2y ago
yes, now it works after I moved to _authenticated directory
No description
equal-jade
equal-jadeOP•2y ago
can you confirm /_authenticated/dashboard this is correct the actual url will be //dashboard in the browser
extended-salmon
extended-salmon•2y ago
Yes, the _layout routes are entirely transparent to the browser. Think of it as a way of grouping related routes together. In this case, the relation is the authenticated status. So /_authenticated/dashboard turns into /dashboard.
equal-jade
equal-jadeOP•2y ago
tanstack router is amazing 😄 thank for you the help

Did you find this page helpful?