callbackURL does not work, google sign in #1506

Closed
opened 2026-03-13 08:44:00 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @erik-kroon on GitHub (Jul 16, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. use better-t-stack to create a monorepo with tanstack-start/hono/drizzle
  2. add google social provider
  3. add button with google social sign in, set callbackURL to "/dashboard"
  4. get redirected to localhost:3000/ instead of localhost:3001/dashboard

Current vs. Expected behavior

one should get redirected to the frontend origin + path derived from callbackURL instead of root of backend

Image

What version of Better Auth are you using?

1.2.8

Provide environment information

OS: macOS 15.5.0
Memory: 16 GiB
Architecture: aarch64
Runtime: Bun

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "../db/database";
import * as authSchemas from "../db/schema";
import {
  polar,
  checkout,
  portal,
  usage,
  webhooks,
} from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";

const polarClient = new Polar({
  accessToken: process.env.POLAR_ACCESS_TOKEN!,
  server: "sandbox",
});

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: authSchemas,
  }),

  plugins: [
    polar({
      client: polarClient,
      createCustomerOnSignUp: true,
      use: [
        checkout({
          products: [{ productId: "prod_starter", slug: "starter" }],
          successUrl: `${process.env.CORS_ORIGIN!}/setup?checkout_id={CHECKOUT_ID}`,
          authenticatedUsersOnly: true,
        }),
        portal(),
        usage(),
        // webhooks({}),
      ],
    }),
  ],

  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
  },

  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60,
    },
  },

  baseURL: process.env.BETTER_AUTH_URL!,
  trustedOrigins: [process.env.CORS_ORIGIN!],
  emailAndPassword: { enabled: true },
});

export type Auth = typeof auth;

Additional context

current janky workaround:

app.get("/", (c) => {
  return c.redirect(process.env.CORS_ORIGIN!);
});
Originally created by @erik-kroon on GitHub (Jul 16, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. use better-t-stack to create a monorepo with tanstack-start/hono/drizzle 2. add google social provider 3. add button with google social sign in, set callbackURL to "/dashboard" 4. get redirected to localhost:3000/ instead of localhost:3001/dashboard ### Current vs. Expected behavior one should get redirected to the frontend origin + path derived from callbackURL instead of root of backend <img width="2940" height="892" alt="Image" src="https://github.com/user-attachments/assets/3aa2b494-fe99-4c0a-a74f-3badaba632a9" /> ### What version of Better Auth are you using? 1.2.8 ### Provide environment information ```bash OS: macOS 15.5.0 Memory: 16 GiB Architecture: aarch64 Runtime: Bun ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "../db/database"; import * as authSchemas from "../db/schema"; import { polar, checkout, portal, usage, webhooks, } from "@polar-sh/better-auth"; import { Polar } from "@polar-sh/sdk"; const polarClient = new Polar({ accessToken: process.env.POLAR_ACCESS_TOKEN!, server: "sandbox", }); export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: authSchemas, }), plugins: [ polar({ client: polarClient, createCustomerOnSignUp: true, use: [ checkout({ products: [{ productId: "prod_starter", slug: "starter" }], successUrl: `${process.env.CORS_ORIGIN!}/setup?checkout_id={CHECKOUT_ID}`, authenticatedUsersOnly: true, }), portal(), usage(), // webhooks({}), ], }), ], socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }, }, session: { cookieCache: { enabled: true, maxAge: 5 * 60, }, }, baseURL: process.env.BETTER_AUTH_URL!, trustedOrigins: [process.env.CORS_ORIGIN!], emailAndPassword: { enabled: true }, }); export type Auth = typeof auth; ``` ### Additional context current janky workaround: ```typescript app.get("/", (c) => { return c.redirect(process.env.CORS_ORIGIN!); }); ```
GiteaMirror added the social-provider label 2026-03-13 08:44:00 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jul 16, 2025):

Hi @erik-kroon! I'm Dosu and I’m helping the better-auth team.

Better Auth validates callbackURL against trustedOrigins and only allows redirects to relative paths or to origins explicitly listed in trustedOrigins. In monorepo setups where your frontend and backend run on different ports or origins, if you set callbackURL to a relative path like /dashboard, Better Auth will redirect to that path on the backend origin by default—not the frontend.

To get the redirect to go to your frontend, set callbackURL to the full frontend URL (e.g., http://localhost:3001/dashboard) and make sure that exact origin (including port) is included in your trustedOrigins array. For example, trustedOrigins should be ["http://localhost:3001"] if that's your frontend. If callbackURL is not absolute or doesn't match a trusted origin, Better Auth will fall back to redirecting to the backend root for security reasons see code reference.

Double-check that your frontend is passing the full callbackURL to the backend during sign-in, and that your backend config includes the correct trustedOrigins. If you want to handle the redirect manually, you can use the disableRedirect option in the signInSocial endpoint.

This setup should ensure you get redirected to the correct frontend URL after authentication.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

@dosubot[bot] commented on GitHub (Jul 16, 2025): <!-- Greeting --> Hi @erik-kroon! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> Better Auth validates callbackURL against trustedOrigins and only allows redirects to relative paths or to origins explicitly listed in trustedOrigins. In monorepo setups where your frontend and backend run on different ports or origins, if you set callbackURL to a relative path like /dashboard, Better Auth will redirect to that path on the backend origin by default—not the frontend. To get the redirect to go to your frontend, set callbackURL to the full frontend URL (e.g., http://localhost:3001/dashboard) and make sure that exact origin (including port) is included in your trustedOrigins array. For example, trustedOrigins should be ["http://localhost:3001"] if that's your frontend. If callbackURL is not absolute or doesn't match a trusted origin, Better Auth will fall back to redirecting to the backend root for security reasons [see code reference](https://github.com/better-auth/better-auth/blob/7364c169f6de0eec4f095112047e23480b240078/packages/better-auth/src/api/routes/sign-in.ts). Double-check that your frontend is passing the full callbackURL to the backend during sign-in, and that your backend config includes the correct trustedOrigins. If you want to handle the redirect manually, you can use the disableRedirect option in the signInSocial endpoint. This setup should ensure you get redirected to the correct frontend URL after authentication. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/f3ed7af7-decc-4672-93e6-ab8fe5cce18a?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3407)
Author
Owner

@erik-kroon commented on GitHub (Jul 16, 2025):

this was simply me being slightly retarded, soz

@erik-kroon commented on GitHub (Jul 16, 2025): this was simply me being slightly retarded, soz
Author
Owner

@nachomglz commented on GitHub (Jan 19, 2026):

Hey, I'm still having this issue, I set the origin properly and everything, and I would expect the redirect to be made to my frontend (port 3000).
I expect this because it's what happens when you do a email + password sign in. See this code:

// Better Auth client (frontend)
const authClient = createAuthClient({
  basePath: '/api/v1/auth',
  baseURL: env.NEXT_PUBLIC_BACKEND_URL,
  ...
})

The code below redirects to the frontend /topics page.

await authClient.signIn.email({
  email: data.email,
  password: data.password,
  callbackURL: '/topics',
})

The code below doesn't redirect to the frontend /topics page. Instead it redirects to the backend /topics route (inexistent)

await authClient.signIn.social({
  provider: 'google',
  callbackURL: '/topics',
})

The API is the same for both, so I would expect the same behavior from both, could you give more context on why there's this difference?

@nachomglz commented on GitHub (Jan 19, 2026): Hey, I'm still having this issue, I set the origin properly and everything, and I would expect the redirect to be made to my frontend (port 3000). I expect this because it's what happens when you do a email + password sign in. See this code: ```typescript // Better Auth client (frontend) const authClient = createAuthClient({ basePath: '/api/v1/auth', baseURL: env.NEXT_PUBLIC_BACKEND_URL, ... }) ``` The code below **redirects** to the frontend /topics page. ```typescript await authClient.signIn.email({ email: data.email, password: data.password, callbackURL: '/topics', }) ``` The code below **doesn't redirect** to the frontend /topics page. Instead it redirects to the backend /topics route (inexistent) ```typescript await authClient.signIn.social({ provider: 'google', callbackURL: '/topics', }) ``` The API is the same for both, so I would expect the same behavior from both, could you give more context on why there's this difference?
Author
Owner

@GiaredL commented on GitHub (Jan 23, 2026):

Im having this issue as well. When you say this was just you being retarded, what did you do to fix it? lol

@GiaredL commented on GitHub (Jan 23, 2026): Im having this issue as well. When you say this was just you being retarded, what did you do to fix it? lol
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1506