[GH-ISSUE #3714] google authentication w/ mcp plugin not triggering redirect to url #9695

Closed
opened 2026-04-13 05:20:29 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @valebearzotti on GitHub (Jul 31, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3714

I've created a custom provider that injects the middleware and router needed to my Express application.

I'm using the toNodeHandler() and passing the auth instance, and everything works fine.

It's until I set up Google as a provider with only the client_id and secret parameters (with the google search console already set up and the callback url pointing to http://127.0.0.1:3002/api/auth/callback/google which is the host & port I'm running my app.

After completing the Auth flow, using the authClient on the frontend as:

    const { error } = await authClient.signIn.social({
      provider: "google",
    });

The flow completes correctly, it creates the cookie, and redirects to the callback URL. This callback URL does not trigger opening, for example, Cursor. Instead it just sends a JSON to the frontend of the following format:

{"redirect":true,"url":"cursor://anysphere.cursor-retrieval/oauth/...correct data"

If I copy and paste that link into a new tab I trigger opening Cursor and complete the flow.

If I don't, and go back to Cursor, click on "Needs login" again, I'll get redirected to /authorize and complete the flow again.

My concern is about the "redirection" not being triggered correctly when using the Google provider.

Originally created by @valebearzotti on GitHub (Jul 31, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3714 I've created a custom provider that injects the middleware and router needed to my Express application. I'm using the toNodeHandler() and passing the auth instance, and everything works fine. It's until I set up Google as a provider with only the `client_id` and `secret` parameters (with the google search console already set up and the callback url pointing to `http://127.0.0.1:3002/api/auth/callback/google` which is the host & port I'm running my app. After completing the Auth flow, using the authClient on the frontend as: ``` const { error } = await authClient.signIn.social({ provider: "google", }); ``` The flow completes correctly, it creates the cookie, and redirects to the callback URL. This callback URL does not trigger opening, for example, Cursor. Instead it just sends a JSON to the frontend of the following format: ``` {"redirect":true,"url":"cursor://anysphere.cursor-retrieval/oauth/...correct data" ``` If I copy and paste that link into a new tab I trigger opening Cursor and complete the flow. If I don't, and go back to Cursor, click on "Needs login" again, I'll get redirected to /authorize and complete the flow again. My concern is about the "redirection" not being triggered correctly when using the Google provider.
GiteaMirror added the locked label 2026-04-13 05:20:29 -05:00
Author
Owner

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

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

This behavior is intentional: the MCP plugin and better-auth framework return a JSON response with the redirect URL (including custom schemes like cursor://) and expect your frontend to handle the redirect. Browsers often block automatic redirects to custom URL schemes for security reasons, so the backend doesn't perform the redirect directly.

To trigger the redirect, your frontend should check for a response like {"redirect": true, "url": "..."} and then programmatically set window.location (or use a similar method) to the provided URL. For example:

const { error, redirect, url } = await authClient.signIn.social({ provider: "google" });
if (redirect && url) {
  window.location = url;
}

If you don't handle this in your frontend, the user won't be navigated to the custom URL scheme and the flow will appear incomplete. This pattern is consistent with SPA/mobile app flows and recent changes in better-auth that expect the client to initiate navigation after authentication citation citation.

If you already have this logic and it's still not working, let me know—there may be browser-specific restrictions or edge cases to consider.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3139690058 --> @dosubot[bot] commented on GitHub (Jul 31, 2025): <!-- Greeting --> Hi @valebearzotti! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This behavior is intentional: the MCP plugin and better-auth framework return a JSON response with the redirect URL (including custom schemes like cursor://) and expect your frontend to handle the redirect. Browsers often block automatic redirects to custom URL schemes for security reasons, so the backend doesn't perform the redirect directly. To trigger the redirect, your frontend should check for a response like {"redirect": true, "url": "..."} and then programmatically set window.location (or use a similar method) to the provided URL. For example: ```js const { error, redirect, url } = await authClient.signIn.social({ provider: "google" }); if (redirect && url) { window.location = url; } ``` If you don't handle this in your frontend, the user won't be navigated to the custom URL scheme and the flow will appear incomplete. This pattern is consistent with SPA/mobile app flows and recent changes in better-auth that expect the client to initiate navigation after authentication [citation](https://github.com/better-auth/better-auth/blob/33b0635c4dc1a28ce217b4eadecc4ed24092f12e/packages/better-auth/src/plugins/mcp/index.ts) [citation](https://github.com/better-auth/better-auth/pull/2641). If you already have this logic and it's still not working, let me know—there may be browser-specific restrictions or edge cases to consider. <!-- 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/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/ff77ecff-1a04-49c8-a7e6-c8a5477601b4?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&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/3714)
Author
Owner

@valebearzotti commented on GitHub (Jul 31, 2025):

@dosu There's no redirect argument being returned from that authClient method call.

Browsers often block automatic redirects to custom URL schemes for security reasons, so the backend doesn't perform the redirect directly.

Why does the /authorize endpoint perform a redirect then? It's not consistent

<!-- gh-comment-id:3139714704 --> @valebearzotti commented on GitHub (Jul 31, 2025): @dosu There's no `redirect` argument being returned from that authClient method call. > Browsers often block automatic redirects to custom URL schemes for security reasons, so the backend doesn't perform the redirect directly. Why does the /authorize endpoint perform a redirect then? It's not consistent
Author
Owner

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

The difference is intentional and comes from how the MCP plugin is designed. The /authorize endpoint (in MCP) usually performs a direct HTTP redirect, as that's standard for OAuth2 authorization flows and is expected by most clients and browsers ref. However, the MCP middleware has a special case: if an internal redirect (HTTP 302) is triggered during certain flows (like after login), it can catch that and return a JSON response with { "redirect": true, "url": ... } instead. This is specifically to support frontend-driven redirects, especially for custom URL schemes or mobile apps where a backend redirect might not work as intended ref.

The /callback endpoint (used for social providers like Google) always performs a server-side HTTP redirect to the callback URL after authentication—there's no JSON response with a redirect URL in this part of the flow ref.

As for the redirect argument: the response from authClient.signIn.social depends on how the backend is configured. If the backend returns a JSON { redirect, url } (which happens if the MCP plugin's middleware catches a 302), you'll get those fields. If the backend does a direct HTTP redirect, your frontend won't see a JSON response at all. This is why you might not see a redirect argument in your current flow—it depends on which endpoint and code path is being hit ref.

So, the inconsistency is by design: MCP's /authorize endpoint can return JSON for frontend-driven redirects, but /callback always does a backend redirect. There isn't a user-facing config to unify this—it's a plugin-level implementation detail.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3139727016 --> @dosubot[bot] commented on GitHub (Jul 31, 2025): <!-- Answer --> The difference is intentional and comes from how the MCP plugin is designed. The /authorize endpoint (in MCP) usually performs a direct HTTP redirect, as that's standard for OAuth2 authorization flows and is expected by most clients and browsers [ref](https://github.com/better-auth/better-auth/blob/33b0635c4dc1a28ce217b4eadecc4ed24092f12e/packages/better-auth/src/plugins/mcp/authorize.ts). However, the MCP middleware has a special case: if an internal redirect (HTTP 302) is triggered during certain flows (like after login), it can catch that and return a JSON response with `{ "redirect": true, "url": ... }` instead. This is specifically to support frontend-driven redirects, especially for custom URL schemes or mobile apps where a backend redirect might not work as intended [ref](https://github.com/better-auth/better-auth/blob/33b0635c4dc1a28ce217b4eadecc4ed24092f12e/packages/better-auth/src/plugins/mcp/index.ts). The /callback endpoint (used for social providers like Google) always performs a server-side HTTP redirect to the callback URL after authentication—there's no JSON response with a redirect URL in this part of the flow [ref](https://github.com/better-auth/better-auth/blob/33b0635c4dc1a28ce217b4eadecc4ed24092f12e/packages/better-auth/src/api/routes/callback.ts). As for the `redirect` argument: the response from `authClient.signIn.social` depends on how the backend is configured. If the backend returns a JSON `{ redirect, url }` (which happens if the MCP plugin's middleware catches a 302), you'll get those fields. If the backend does a direct HTTP redirect, your frontend won't see a JSON response at all. This is why you might not see a `redirect` argument in your current flow—it depends on which endpoint and code path is being hit [ref](https://github.com/better-auth/better-auth/issues/2657). So, the inconsistency is by design: MCP's /authorize endpoint can return JSON for frontend-driven redirects, but /callback always does a backend redirect. There isn't a user-facing config to unify this—it's a plugin-level implementation detail. <!-- 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/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/7ed6d975-97ae-4bb4-b5f2-54543716405c?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&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/3714)
Author
Owner

@frectonz commented on GitHub (Sep 3, 2025):

@valebearzotti are you still facing this issue, has it been fixed by #3838

<!-- gh-comment-id:3249306882 --> @frectonz commented on GitHub (Sep 3, 2025): @valebearzotti are you still facing this issue, has it been fixed by #3838
Author
Owner

@valebearzotti commented on GitHub (Sep 3, 2025):

hey @frectonz ! thanks for reaching out
I came up with a workaround where I manually change the callback and trigger the redirect. I should try updating and see if that fixes it.

<!-- gh-comment-id:3249573267 --> @valebearzotti commented on GitHub (Sep 3, 2025): hey @frectonz ! thanks for reaching out I came up with a workaround where I manually change the callback and trigger the redirect. I should try updating and see if that fixes it.
Author
Owner

@dosubot[bot] commented on GitHub (Dec 3, 2025):

Hi, @valebearzotti. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that after implementing Google auth with the MCP plugin, the post-login redirect returns JSON with a redirect URL instead of automatically redirecting.
  • It was clarified that this behavior is intentional for backend-to-frontend handling, especially for custom URL schemes.
  • You noted that no redirect argument is returned in your flow, which depends on the endpoint and plugin design.
  • The maintainer asked if the issue persists or was fixed by PR #3838.
  • You shared a manual redirect workaround and planned to test the update.

Next Steps:

  • Please let me know if this issue is still relevant with the latest version of better-auth by commenting here.
  • If I don’t hear back within 7 days, I will automatically close this issue.

Thanks for your understanding and contribution!

<!-- gh-comment-id:3607641983 --> @dosubot[bot] commented on GitHub (Dec 3, 2025): Hi, @valebearzotti. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You reported that after implementing Google auth with the MCP plugin, the post-login redirect returns JSON with a redirect URL instead of automatically redirecting. - It was clarified that this behavior is intentional for backend-to-frontend handling, especially for custom URL schemes. - You noted that no redirect argument is returned in your flow, which depends on the endpoint and plugin design. - The maintainer asked if the issue persists or was fixed by PR #3838. - You shared a manual redirect workaround and planned to test the update. **Next Steps:** - Please let me know if this issue is still relevant with the latest version of better-auth by commenting here. - If I don’t hear back within 7 days, I will automatically close this issue. Thanks for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9695