T
TanStack11mo ago
genetic-orange

Removing sleep hack from the Router Authenticated Routes Example

In the login handler below: https://stackblitz.com/edit/github-ystzdp?file=src%2Froutes%2Flogin.tsx There is a sleep with the comment:
// This is just a hack being used to wait for the auth state to update in a real app, you'd want to use a more robust solution
await sleep(1)
// This is just a hack being used to wait for the auth state to update in a real app, you'd want to use a more robust solution
await sleep(1)
Without this sleep, context.auth.isAuthenticated remains false in the beforeLoad of the route being redirected to causing a redirection back to the login page. What would be the more robust solution without using the sleep?
9 Replies
automatic-azure
automatic-azure11mo ago
Wait for the auth to be ready before rendering the router. I am using this with react-oidc-context
import { RouterProvider } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";

import router from "@/services/router";

const App = () => {
const auth = useAuth();

if (auth.isLoading) return null;

return <RouterProvider router={router} context={{ auth }} />;
};

export default App;
import { RouterProvider } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";

import router from "@/services/router";

const App = () => {
const auth = useAuth();

if (auth.isLoading) return null;

return <RouterProvider router={router} context={{ auth }} />;
};

export default App;
or wait for auth to be ready in the beforeLoad, same example from above, but we create a promise which we can await in the beforeLoad.
// This code is not tested, I just thought of this now
import { useRef } from 'react'
import { RouterProvider } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";

import router from "@/services/router";

const createAuthPromise = () => {
let resolve;
const promise = new Promise((_resolve) => {
resolve = _resolve
})

return {resolve, promise}
}

const App = () => {
const auth = useAuth();
const authPromiseRef = useRef(createAuthPromise())

useEffect(() => {
if (auth.isLoading) return

authPromiseRef.current.resolve()
}, [auth.isLoading])

return <RouterProvider router={router} context={{ auth, authPromise: authPromiseRef.current.promise }} />;
};

export default App;
// This code is not tested, I just thought of this now
import { useRef } from 'react'
import { RouterProvider } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";

import router from "@/services/router";

const createAuthPromise = () => {
let resolve;
const promise = new Promise((_resolve) => {
resolve = _resolve
})

return {resolve, promise}
}

const App = () => {
const auth = useAuth();
const authPromiseRef = useRef(createAuthPromise())

useEffect(() => {
if (auth.isLoading) return

authPromiseRef.current.resolve()
}, [auth.isLoading])

return <RouterProvider router={router} context={{ auth, authPromise: authPromiseRef.current.promise }} />;
};

export default App;
genetic-orange
genetic-orangeOP11mo ago
How would this look in the Router authenticated route example?
automatic-azure
automatic-azure11mo ago
It depends on where your authentication comes from This does not seem to work with react-oidc-context, I will investigate further and will report back I managed to get it working
// In App component
import { RouterProvider } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";

import router from "@/services/router";
import { useRef } from "react";
import { useEffect } from "react";

const createAuthPromise = () => {
let resolve;

const promise = new Promise((_resolve) => {
resolve = (isAuthenticated) => _resolve(isAuthenticated);
});

return { resolve, promise };
};

const App = () => {
const auth = useAuth();
const authPromise = useRef(createAuthPromise());

useEffect(() => {
if (auth.isLoading) return;

authPromise.current.resolve(auth.isAuthenticated);
}, [auth.isLoading, auth.isAuthenticated]);

return (
<RouterProvider
router={router}
context={{ auth, authPromise: authPromise.current.promise }}
/>
);
};

export default App;

// In route
export const Route = createFileRoute('/_Auth')({
beforeLoad: async ({ context }) => {
const isAuthenticated = await context.authPromise;
if (isAuthenticated) return;

await context.auth.signinRedirect();
},
// ...
})
// In App component
import { RouterProvider } from "@tanstack/react-router";
import { useAuth } from "react-oidc-context";

import router from "@/services/router";
import { useRef } from "react";
import { useEffect } from "react";

const createAuthPromise = () => {
let resolve;

const promise = new Promise((_resolve) => {
resolve = (isAuthenticated) => _resolve(isAuthenticated);
});

return { resolve, promise };
};

const App = () => {
const auth = useAuth();
const authPromise = useRef(createAuthPromise());

useEffect(() => {
if (auth.isLoading) return;

authPromise.current.resolve(auth.isAuthenticated);
}, [auth.isLoading, auth.isAuthenticated]);

return (
<RouterProvider
router={router}
context={{ auth, authPromise: authPromise.current.promise }}
/>
);
};

export default App;

// In route
export const Route = createFileRoute('/_Auth')({
beforeLoad: async ({ context }) => {
const isAuthenticated = await context.authPromise;
if (isAuthenticated) return;

await context.auth.signinRedirect();
},
// ...
})
anyway @Michael Wolfenden if you want help with your problem you need to specify how your auth works, or provide us with the tool/library you use for auth. This is because auth can be implemented in multiple ways: - An HTTP request somewhere to fetch auth data - Libraries like react-oidc-context which provide you with an auth object (Like I used above) The tanstack router docs specify auth handling here, but these are basic examples.
genetic-orange
genetic-orangeOP11mo ago
Thanks for your help @ferretwithabéret . I'm using react query. I have a cutdown sample of what I'm doing here: https://stackblitz.com/edit/github-qqa8eu-ronaka?file=src%2Fauth.tsx It suffers from the same issue, if you remove the sleep it breaks
Michael Wolfenden
StackBlitz
Router Authenticated Routes React Query - StackBlitz
Run official live example code for Router Authenticated Routes, created by Tanstack on StackBlitz
automatic-azure
automatic-azure11mo ago
In your case, you could rather use queryClient.getQueryData in the beforeLoad instead of the auth, from what I could gather the context does not update in time for the invalidate to take the isAuthenticated as true and it uses it as it's last value of false
genetic-orange
genetic-orangeOP11mo ago
Which defeats the whole point of using context in the first place
optimistic-gold
optimistic-gold11mo ago
https://discord.com/channels/719702312431386674/1293299371001122896 Related conversation: https://discord.com/channels/719702312431386674/1007702008448426065/1297574468503343185 This looks like a very common question. I hope that the community can come up with a good solution and share it on the examples repo
optimistic-gold
optimistic-gold11mo ago
Is there any official response to this by Tanner?
dependent-tan
dependent-tan11mo ago
nothing yet

Did you find this page helpful?