BA
Better Auth•2w ago
bxr

sign in stuck loading in deployed app

Hi there, I have an issue I am encountering in my deployed app but not local app. When I try to sign-in the user gets successfully authenticated but the loading button is stuck loading. This issue does not happen locally (of course 😂 ) and I am not too sure why that's happening. I know the request response shows redirect = false but the same is shown locally and the user gets redirected to their profile. I use @daveycodez's better-auth library but I don't think that's the issue
function RouteComponent() {
const { pathname } = Route.useParams();

return (
<>
<AuthCard
pathname={pathname}
socialLayout="auto"
redirectTo="/profile"
classNames={{ title: "text-center", description: "text-center" }}
/>
</>
);
}
function RouteComponent() {
const { pathname } = Route.useParams();

return (
<>
<AuthCard
pathname={pathname}
socialLayout="auto"
redirectTo="/profile"
classNames={{ title: "text-center", description: "text-center" }}
/>
</>
);
}
could it be a different issue? I also don't see the session being set in the in the browser cookies which leads to me believe that could be an issue as well. I use tanstack start but with a separate elysiajs backend where better-auth is located
22 Replies
bxr
bxrOP•2w ago
bxr
bxrOP•2w ago
Both my backend and frontend are in different domains, I noticed in the hono docs you can set sameSite=none but it still does not work
Hono Integration | Better Auth
Integrate Better Auth with Hono.
bxr
bxrOP•2w ago
I see it's cookie issues, i'll figure it out.
bxr
bxrOP•2w ago
Actually I fixed the cookies but redirect still is not happening :dead:
No description
No description
Ping
Ping•2w ago
I have no clue why it's not redirecting. 🤔 @bekacru , do you think you can help with this case?
daveycodez
daveycodez•2w ago
This could be an issue with middleware caching Can you show your AuthUIProvider
bxr
bxrOP•2w ago
Yep it’s in my __root route
}),

component: () => {
const router = useRouter();
return (
<AuthQueryProvider>
<AuthUIProviderTanstack
authClient={authClient}
providers={["discord"]}
basePath="/"
baseURL={env.VITE_AGORA_URL}
navigate={(href) => router.navigate({ href })}
replace={(href) => router.navigate({ href, replace: true })}
Link={({ href, ...props }) => <Link to={href} {...props} />}
localization={{
signInDescription: "Login with your Discord or email",
}}
>
<RootDocument>
<Outlet />
<Toaster />
<TanStackRouterDevtools position="bottom-right" />
<ReactQueryDevtools buttonPosition="top-right" />
</RootDocument>
</AuthUIProviderTanstack>
</AuthQueryProvider>
);
},
beforeLoad: async ({ context }) => {
const user = await context.queryClient.ensureQueryData({
queryKey: ["user"],
queryFn: ({ signal }) => getUser({ signal }),
});
return { user };
},
}),

component: () => {
const router = useRouter();
return (
<AuthQueryProvider>
<AuthUIProviderTanstack
authClient={authClient}
providers={["discord"]}
basePath="/"
baseURL={env.VITE_AGORA_URL}
navigate={(href) => router.navigate({ href })}
replace={(href) => router.navigate({ href, replace: true })}
Link={({ href, ...props }) => <Link to={href} {...props} />}
localization={{
signInDescription: "Login with your Discord or email",
}}
>
<RootDocument>
<Outlet />
<Toaster />
<TanStackRouterDevtools position="bottom-right" />
<ReactQueryDevtools buttonPosition="top-right" />
</RootDocument>
</AuthUIProviderTanstack>
</AuthQueryProvider>
);
},
beforeLoad: async ({ context }) => {
const user = await context.queryClient.ensureQueryData({
queryKey: ["user"],
queryFn: ({ signal }) => getUser({ signal }),
});
return { user };
},
daveycodez
daveycodez•2w ago
If you redirect to home page instead of profile does it work It probably has to do with your route protection using a different query key than the session hook from better Auth TanStack Tries to redirect to profile but “user” wasn’t refetched. Only “session” was refetched You’re also probably getting double fetches to session now
daveycodez
daveycodez•2w ago
GitHub
GitHub - daveyplate/better-auth-tanstack
Contribute to daveyplate/better-auth-tanstack development by creating an account on GitHub.
daveycodez
daveycodez•2w ago
The default query key for session is [“session”] So yea what’s happening is on sign in, [“session”] is what gets refetched automatically by the AuthUIProviderTanstack, and your protected route is checking a custom [“user”] key I assume your profile page checks if no user then redirect to sign in. That’s why it looks like no redirect happens But yea I would advise against having separate key for user from session to avoid double queries. And just having a session key only
bxr
bxrOP•2w ago
I see I wasn’t aware a session key was already set up with your library Weird that the issue doesn’t happen locally though I think you have an example tanstack start repo but that didn’t have any prefetch ssr set up so I just did this
daveycodez
daveycodez•2w ago
I think it's because of how the onLoad functions in prod vs dev Not totally sure, Next.js had similar issues Yea I've been wanting to update the repo with SSR example but haven't been sure of the best way with TSS yet But yea even if you had manually set up a login form you would've ran into this issue, need to refetch the user before redirecting since it will still be empty and fail
bxr
bxrOP•2w ago
I’ll figure something out, thanks for all your help thus far
bxr
bxrOP•2w ago
I am trying something here... similar to clerk's tss handler I tried this:
// better-auth-ssr-handler
import type { AnyRouter } from "@tanstack/react-router";
import type { EventHandler } from "@tanstack/react-start/server";
import { getSession } from "@/lib/server/auth";
import type { QueryClient } from "@tanstack/react-query";

export type HandlerCallback<TRouter extends AnyRouter> = (ctx: {
request: Request;
router: TRouter;
responseHeaders: Headers;
}) => Response | Promise<Response>;

export type CustomizeStartHandler<TRouter extends AnyRouter> = (
cb: HandlerCallback<TRouter>,
) => EventHandler;

export function withBetterAuth<TRouter extends AnyRouter>(
startHandler: CustomizeStartHandler<TRouter>,
) {
return (cb: HandlerCallback<TRouter>): EventHandler =>
startHandler(async ({ request, router, responseHeaders }) => {
const qc = router.options.context.queryClient as QueryClient;

const session = await getSession();

qc.setQueryData(["session"], session);

// 4) stash session + user into router context
router.update({
context: {
...router.options.context,
session,
},
});

// 5) run through all loaders / beforeLoad
await router.load();

// 6) hand back to your normal handler (e.g. defaultStreamHandler)
return cb({ request, router, responseHeaders });
});
}

//ssr.tsx
import {
createStartHandler,
defaultStreamHandler,
} from "@tanstack/react-start/server";
import { getRouterManifest } from "@tanstack/react-start/router-manifest";

import { createRouter } from "./router";
import { withBetterAuth } from "@/lib/ssr/auth";

export default withBetterAuth(
createStartHandler({
createRouter,
getRouterManifest,
}),
)(defaultStreamHandler);
// better-auth-ssr-handler
import type { AnyRouter } from "@tanstack/react-router";
import type { EventHandler } from "@tanstack/react-start/server";
import { getSession } from "@/lib/server/auth";
import type { QueryClient } from "@tanstack/react-query";

export type HandlerCallback<TRouter extends AnyRouter> = (ctx: {
request: Request;
router: TRouter;
responseHeaders: Headers;
}) => Response | Promise<Response>;

export type CustomizeStartHandler<TRouter extends AnyRouter> = (
cb: HandlerCallback<TRouter>,
) => EventHandler;

export function withBetterAuth<TRouter extends AnyRouter>(
startHandler: CustomizeStartHandler<TRouter>,
) {
return (cb: HandlerCallback<TRouter>): EventHandler =>
startHandler(async ({ request, router, responseHeaders }) => {
const qc = router.options.context.queryClient as QueryClient;

const session = await getSession();

qc.setQueryData(["session"], session);

// 4) stash session + user into router context
router.update({
context: {
...router.options.context,
session,
},
});

// 5) run through all loaders / beforeLoad
await router.load();

// 6) hand back to your normal handler (e.g. defaultStreamHandler)
return cb({ request, router, responseHeaders });
});
}

//ssr.tsx
import {
createStartHandler,
defaultStreamHandler,
} from "@tanstack/react-start/server";
import { getRouterManifest } from "@tanstack/react-start/router-manifest";

import { createRouter } from "./router";
import { withBetterAuth } from "@/lib/ssr/auth";

export default withBetterAuth(
createStartHandler({
createRouter,
getRouterManifest,
}),
)(defaultStreamHandler);
I think it's a step forward, in dev it doesn't work yet i notice that the cookie is set but the user session disappears after the client loads
GitHub
javascript/packages/tanstack-react-start/src/server/middlewareHandl...
Official JavaScript repository for Clerk authentication - clerk/javascript
bxr
bxrOP•2w ago
also the auth pages populates the query key but after that it's gone
No description
bxr
bxrOP•2w ago
I guess the issue is that the cache for the session is only on the server and not passed to the client on hydration
daveycodez
daveycodez•2w ago
I would look into TS Query guide for hydrating query clients from server to client TBH though, why preload the session in SSR anyways I'd just make it client only, check for cookie presence for optimistic SSR redirects Do actual route protection client side, optimistic protection checking for cookie presence in SSR, and just fetch session on the client You only really need to get session on the server side for server actions or API routes that need to be protected for mutations Makes your code base way more simple too, and you don't really lose on performance or functionality anyways. You can just do getSessionCookie on your SSR loader and do redirects to skip the client from rendering if the cookie is present. Then just use the useSession hook to track the auth state on the client The other thing about dynamic SSR like this is you opt out of static rendering which is the fastest way to load a page
bxr
bxrOP•2w ago
That makes sense. Optimistic protection would be on the beforeload? How would you do route protection on the client side? A quick example would make me understand a bit better if you have any 😄
daveycodez
daveycodez•2w ago
You just show a skeleton while useSession is pending and don’t render until session is present Then use effect watching session, redirect the page to sign in in that use effect if isPending is false and !session Better Auth UI provides a lot of helpers for doing this
bxr
bxrOP•2w ago
ah yeah you have that useAuthenticate hook
daveycodez
daveycodez•2w ago
Yea that’s the easiest way to do a redirect since it also will send the user back to the page after sign in using redirectTo url param
bxr
bxrOP•2w ago
Thanks, removing all beforeloads from the router helped. - hopefully i can figure this out soon :lolsob:

Did you find this page helpful?