From f7e32006be4d0417fb329a3e2aab269aa6469e7f Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 22 Sep 2025 17:47:16 -0700 Subject: [PATCH] fix (oauth-proxy): use of productionURL in for callback comparison (#4825) --- .../src/plugins/oauth-proxy/index.ts | 7 +- .../plugins/oauth-proxy/oauth-proxy.test.ts | 73 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/packages/better-auth/src/plugins/oauth-proxy/index.ts b/packages/better-auth/src/plugins/oauth-proxy/index.ts index 91d0d20c3c..de218c445a 100644 --- a/packages/better-auth/src/plugins/oauth-proxy/index.ts +++ b/packages/better-auth/src/plugins/oauth-proxy/index.ts @@ -56,6 +56,7 @@ export const oAuthProxy = (opts?: OAuthProxyOptions) => { return { id: "oauth-proxy", + options: opts, endpoints: { oAuthProxy: createAuthEndpoint( "/oauth-proxy-callback", @@ -157,7 +158,11 @@ export const oAuthProxy = (opts?: OAuthProxyOptions) => { * We don't want to redirect to the proxy URL if the origin is the same * as the current URL */ - if (origin === getOrigin(ctx.context.baseURL)) { + const productionURL = + opts?.productionURL || + ctx.context.options.baseURL || + ctx.context.baseURL; + if (origin === getOrigin(productionURL)) { const newLocation = locationURL.searchParams.get("callbackURL"); if (!newLocation) { return; diff --git a/packages/better-auth/src/plugins/oauth-proxy/oauth-proxy.test.ts b/packages/better-auth/src/plugins/oauth-proxy/oauth-proxy.test.ts index 764501cf6c..427bcccb22 100644 --- a/packages/better-auth/src/plugins/oauth-proxy/oauth-proxy.test.ts +++ b/packages/better-auth/src/plugins/oauth-proxy/oauth-proxy.test.ts @@ -113,4 +113,77 @@ describe("oauth-proxy", async () => { }, }); }); + + it("should proxy to the original request url", async () => { + const { client } = await getTestInstance({ + baseURL: "https://myapp.com", + plugins: [ + oAuthProxy({ + productionURL: "https://login.myapp.com", + }), + ], + socialProviders: { + google: { + clientId: "test", + clientSecret: "test", + }, + }, + }); + const res = await client.signIn.social( + { + provider: "google", + callbackURL: "/dashboard", + }, + { + throw: true, + }, + ); + const state = new URL(res.url!).searchParams.get("state"); + await client.$fetch(`/callback/google?code=test&state=${state}`, { + onError(context) { + const location = context.response.headers.get("location"); + if (!location) { + throw new Error("Location header not found"); + } + expect(location).toContain( + "https://myapp.com/api/auth/oauth-proxy-callback?callbackURL=%2Fdashboard", + ); + const cookies = new URL(location).searchParams.get("cookies"); + expect(cookies).toBeTruthy(); + }, + }); + }); + + it("shouldn't redirect to proxy url on same origin", async () => { + const { client } = await getTestInstance({ + baseURL: "https://myapp.com", + plugins: [oAuthProxy()], + socialProviders: { + google: { + clientId: "test", + clientSecret: "test", + }, + }, + }); + const res = await client.signIn.social( + { + provider: "google", + callbackURL: "/dashboard", + }, + { + throw: true, + }, + ); + const state = new URL(res.url!).searchParams.get("state"); + await client.$fetch(`/callback/google?code=test&state=${state}`, { + onError(context) { + const location = context.response.headers.get("location"); + if (!location) { + throw new Error("Location header not found"); + } + expect(location).not.toContain("/api/auth/oauth-proxy-callback"); + expect(location).toContain("/dashboard"); + }, + }); + }); });