Redirection after sign in #1471

Closed
opened 2026-03-13 08:42:01 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @wxl306 on GitHub (Jul 7, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

exactly same questions from :
So I have better auth running on my express server. and better auth client on my Nextjs frontend. Express server is running on localhost:4000 and Nextjs on localhost:3000

The issue is after I initiate google login from NextJs (3000 port), the login is successful, however I get redirected to localhost:4000 as in the server URL.

For example:
authClient.signIn.social({
provider: "google",
callbackUrl: "/todo"
})

With this the expected behavior is to get redirected to lcoalhost:3000/todo after login. But I am getting redirected to localhost:4000/todo.

Now I figured that, this is happening because google console has authorized redirect URI set to http://localhost:4000/api/auth/callback/google since localhost:4000 is the one that is making the request.

So I have to pass the full frontend URL (localhost:3000/todo) as callback.

does it mean we have to set up the full callbackurl address when we are using monorepo proejct to seperate the client and server?

Current vs. Expected behavior

current localhost:4000/todo,expected behavior is to get redirected to lcoalhost:3000/todo after login

What version of Better Auth are you using?

1.2.12

Provide environment information

WINDOWS 10
CHORME

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

Client

Auth config (if applicable)


Additional context

No response

Originally created by @wxl306 on GitHub (Jul 7, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce exactly same questions from [:](https://www.answeroverflow.com/m/1364993857653309531) So I have better auth running on my express server. and better auth client on my Nextjs frontend. Express server is running on localhost:4000 and Nextjs on localhost:3000 The issue is after I initiate google login from NextJs (3000 port), the login is successful, **however I get redirected to localhost:4000 as in the server URL**. For example: authClient.signIn.social({ provider: "google", **callbackUrl: "/todo"** }) With this the expected behavior is to get redirected to lcoalhost:3000/todo after login. But I am getting redirected to localhost:4000/todo. Now I figured that, this is happening because google console has authorized redirect URI set to http://localhost:4000/api/auth/callback/google since localhost:4000 is the one that is making the request. So I have to pass the full frontend URL (localhost:3000/todo) as callback. does it mean we **have to set up the full callbackurl address** when we are using monorepo proejct to **seperate the client and server**? ### Current vs. Expected behavior current localhost:4000/todo,expected behavior is to get redirected to lcoalhost:3000/todo after login ### What version of Better Auth are you using? 1.2.12 ### Provide environment information ```bash WINDOWS 10 CHORME ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript ``` ### Additional context _No response_
GiteaMirror added the expressnextjs labels 2026-03-13 08:42:01 -05:00
Author
Owner

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

This happens because when your client and server run on different origins (localhost:3000 for Next.js, localhost:4000 for Express), passing a relative callback URL like /todo will always redirect to the server's origin (localhost:4000/todo). To redirect users back to your frontend after login, you must provide the full absolute callback URL (e.g., http://localhost:3000/todo) in your sign-in call. This is by design—Better Auth resolves relative callback URLs against the server's origin, and there's no built-in way for it to infer your frontend's URL in a multi-origin setup. This pattern is common in OAuth flows and is necessary due to browser security and cross-origin cookie policies discussion.

For best results: always pass the full frontend URL as the callback when your client and server are on different origins. Make sure your Google OAuth redirect URI is set to your backend (e.g., http://localhost:4000/api/auth/callback/google), and your Better Auth config uses the correct baseURL for the API issue. Recent updates in Better Auth have improved callback URL handling, but the requirement to use an absolute URL for cross-origin redirects remains pull request.

If you deploy to production, consider using subdomains (like app.example.com and api.example.com) to simplify cookie and redirect handling discussion.

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 7, 2025): <!-- Answer --> This happens because when your client and server run on different origins (localhost:3000 for Next.js, localhost:4000 for Express), passing a relative callback URL like `/todo` will always redirect to the server's origin (localhost:4000/todo). To redirect users back to your frontend after login, you must provide the full absolute callback URL (e.g., `http://localhost:3000/todo`) in your sign-in call. This is by design—Better Auth resolves relative callback URLs against the server's origin, and there's no built-in way for it to infer your frontend's URL in a multi-origin setup. This pattern is common in OAuth flows and is necessary due to browser security and cross-origin cookie policies [discussion](https://github.com/better-auth/better-auth/discussions/3262). For best results: always pass the full frontend URL as the callback when your client and server are on different origins. Make sure your Google OAuth redirect URI is set to your backend (e.g., `http://localhost:4000/api/auth/callback/google`), and your Better Auth config uses the correct `baseURL` for the API [issue](https://github.com/better-auth/better-auth/issues/2710). Recent updates in Better Auth have improved callback URL handling, but the requirement to use an absolute URL for cross-origin redirects remains [pull request](https://github.com/better-auth/better-auth/pull/2740). If you deploy to production, consider using subdomains (like `app.example.com` and `api.example.com`) to simplify cookie and redirect handling [discussion](https://github.com/better-auth/better-auth/discussions/3262). <!-- 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/0b08f69e-e513-49eb-883e-b2f6f0dee92d?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0b08f69e-e513-49eb-883e-b2f6f0dee92d?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0b08f69e-e513-49eb-883e-b2f6f0dee92d?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0b08f69e-e513-49eb-883e-b2f6f0dee92d?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0b08f69e-e513-49eb-883e-b2f6f0dee92d?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0b08f69e-e513-49eb-883e-b2f6f0dee92d?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/0b08f69e-e513-49eb-883e-b2f6f0dee92d?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/3289)
Author
Owner

@piscopancer commented on GitHub (Jul 7, 2025):

Same question... if i change callbackUrl to http://localhost:3000/studio, I get an error saying it's incorrect as i try to call the singIn function

{"code":"INVALID_CALLBACKURL","message":"Invalid callbackURL"}

i feel like the docs is incomplete when it comes to different domains and ports, I am running Express on :8000 and Vite app on :3000, so there's no way I want to redirect to a non-existent express/dashboard or smth, feels to me the docs was written with only nextjs in mind

@piscopancer commented on GitHub (Jul 7, 2025): Same question... if i change `callbackUrl` to `http://localhost:3000/studio`, I get an error saying it's incorrect as i try to call the `singIn` function ```json {"code":"INVALID_CALLBACKURL","message":"Invalid callbackURL"} ``` i feel like the docs is incomplete when it comes to different domains and ports, I am running Express on :8000 and Vite app on :3000, so there's no way I want to redirect to a non-existent express/dashboard or smth, feels to me the docs was written with only nextjs in mind
Author
Owner

@wxl306 commented on GitHub (Jul 8, 2025):

Same question... if i change callbackUrl to http://localhost:3000/studio, I get an error saying it's incorrect as i try to call the singIn function

{"code":"INVALID_CALLBACKURL","message":"Invalid callbackURL"}

i feel like the docs is incomplete when it comes to different domains and ports, I am running Express on :8000 and Vite app on :3000, so there's no way I want to redirect to a non-existent express/dashboard or smth, feels to me the docs was written with only nextjs in mind

If you assign full url like http://localhost:3000/studio to callback url in authclient signin method,it should be ok, it works for me.
Would you mind share more code ?

@wxl306 commented on GitHub (Jul 8, 2025): > Same question... if i change `callbackUrl` to `http://localhost:3000/studio`, I get an error saying it's incorrect as i try to call the `singIn` function > ```json > {"code":"INVALID_CALLBACKURL","message":"Invalid callbackURL"} > ``` > i feel like the docs is incomplete when it comes to different domains and ports, I am running Express on :8000 and Vite app on :3000, so there's no way I want to redirect to a non-existent express/dashboard or smth, feels to me the docs was written with only nextjs in mind > If you assign full url like http://localhost:3000/studio to callback url in authclient signin method,it should be ok, it works for me. Would you mind share more code ?
Author
Owner

@piscopancer commented on GitHub (Jul 8, 2025):

@wxl306 yeah

"better-auth": "^1.2.12"
export const auth = createAuthClient({
  baseURL: 'http://localhost:8484',
})
const res = await auth.signIn.social({
  provider: 'github',
  callbackURL: 'http://localhost:3000/studio',
})
console.log(res)

Image

@piscopancer commented on GitHub (Jul 8, 2025): @wxl306 yeah ```json "better-auth": "^1.2.12" ``` ```ts export const auth = createAuthClient({ baseURL: 'http://localhost:8484', }) ``` ```ts const res = await auth.signIn.social({ provider: 'github', callbackURL: 'http://localhost:3000/studio', }) console.log(res) ``` ![Image](https://github.com/user-attachments/assets/96d21e7e-ad0f-4cfc-98c0-503ee2db1590)
Author
Owner

@piscopancer commented on GitHub (Jul 8, 2025):

br i should have also specified trusted origins 💀

trustedOrigins: ['http://localhost:3000']

now it does not throw 👌

@piscopancer commented on GitHub (Jul 8, 2025): br i should have also specified trusted origins 💀 ```ts trustedOrigins: ['http://localhost:3000'] ``` now it does not throw 👌
Author
Owner

@ping-maxwell commented on GitHub (Jul 8, 2025):

Hey guys, I'll open a PR to improve the error message regarding this.
I will also close this issue for now, please let me know if there are any other concerns regarding this issue and I'll happily reopen it!

For those who are still facing this issue, you can solve it by adding frontend the domain to the trustedOrigins list and making sure the callbackURL is a full absolute path. Like @piscopancer shows.

@ping-maxwell commented on GitHub (Jul 8, 2025): Hey guys, I'll open a PR to improve the error message regarding this. I will also close this issue for now, please let me know if there are any other concerns regarding this issue and I'll happily reopen it! For those who are still facing this issue, you can solve it by adding frontend the domain to the trustedOrigins list and making sure the callbackURL is a full absolute path. Like @piscopancer shows.
Author
Owner

@ping-maxwell commented on GitHub (Jul 8, 2025):

Hey @piscopancer do you mind sending me your auth config? I'm curious if you're using any custom plugin or what your config looks like.
I can't really find that exact error you're facing with the invalid callback url so maybe your config can give me some hint

@ping-maxwell commented on GitHub (Jul 8, 2025): Hey @piscopancer do you mind sending me your auth config? I'm curious if you're using any custom plugin or what your config looks like. I can't really find that exact error you're facing with the invalid callback url so maybe your config can give me some hint
Author
Owner

@piscopancer commented on GitHub (Jul 9, 2025):

@ping-maxwell here's my config, no plugins

export const auth = betterAuth({
  advanced: {
    cookiePrefix: 'jalyk',
    useSecureCookies: true,
  },
  trustedOrigins: ['http://localhost:3000'],
  session: {
    expiresIn: 60 * 60 * 24 * 365,
  },
  baseURL: 'http://localhost:8484',
  socialProviders: {
    github: {
      clientId: '...',
      clientSecret: '...',
    },
    google: {
      clientId: '...',
      clientSecret: '...',
    },
  },
})
@piscopancer commented on GitHub (Jul 9, 2025): @ping-maxwell here's my config, no plugins ```ts export const auth = betterAuth({ advanced: { cookiePrefix: 'jalyk', useSecureCookies: true, }, trustedOrigins: ['http://localhost:3000'], session: { expiresIn: 60 * 60 * 24 * 365, }, baseURL: 'http://localhost:8484', socialProviders: { github: { clientId: '...', clientSecret: '...', }, google: { clientId: '...', clientSecret: '...', }, }, }) ```
Author
Owner

@dingkai031 commented on GitHub (Dec 27, 2025):

br i should have also specified trusted origins 💀

trustedOrigins: ['http://localhost:3000']
now it does not throw 👌

I have this issue just now using the latest better auth. And I wasted 2 hour playing with my cors configuration :(. But I just need to add my FE to trusted origin.

Hopely for development, please add error message like "localhost:30001 is not part of the trusted origins".

@dingkai031 commented on GitHub (Dec 27, 2025): > br i should have also specified trusted origins 💀 > > trustedOrigins: ['http://localhost:3000'] > now it does not throw 👌 I have this issue just now using the latest better auth. And I wasted 2 hour playing with my cors configuration :(. But I just need to add my FE to trusted origin. Hopely for development, please add error message like "localhost:30001 is not part of the trusted origins".
Author
Owner

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

This happens because when your client and server run on different origins (localhost:3000 for Next.js, localhost:4000 for Express), passing a relative callback URL like /todo will always redirect to the server's origin (localhost:4000/todo). To redirect users back to your frontend after login, you must provide the full absolute callback URL (e.g., http://localhost:3000/todo) in your sign-in call. This is by design—Better Auth resolves relative callback URLs against the server's origin, and there's no built-in way for it to infer your frontend's URL in a multi-origin setup. This pattern is common in OAuth flows and is necessary due to browser security and cross-origin cookie policies discussion.

This would make sense if the behavior was the same through the entire API, but when you use the exact same method but for email + password login, specifying a relative path it does redirect to the https://frontend/page instead of https://backend/page.

Why is this? I couldn't find anything in the docs regarding this difference.

@nachomglz commented on GitHub (Jan 19, 2026): > This happens because when your client and server run on different origins (localhost:3000 for Next.js, localhost:4000 for Express), passing a relative callback URL like `/todo` will always redirect to the server's origin (localhost:4000/todo). To redirect users back to your frontend after login, you must provide the full absolute callback URL (e.g., `http://localhost:3000/todo`) in your sign-in call. This is by design—Better Auth resolves relative callback URLs against the server's origin, and there's no built-in way for it to infer your frontend's URL in a multi-origin setup. This pattern is common in OAuth flows and is necessary due to browser security and cross-origin cookie policies [discussion](https://github.com/better-auth/better-auth/discussions/3262). This would make sense if the behavior was the same through the entire API, but when you use the exact same method but for email + password login, specifying a relative path it does redirect to the https://frontend/page instead of https://backend/page. Why is this? I couldn't find anything in the docs regarding this difference.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1471