mirror of
https://github.com/better-auth/better-auth.git
synced 2026-07-20 00:33:45 -05:00
fix(oauth-proxy): forward result.error verbatim in callback redirect (#9723)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"better-auth": patch
|
||||
---
|
||||
|
||||
The `oauth-proxy` callback now forwards `result.error` from `handleOAuthUserInfo` as the `?error=` query value (e.g. `?error=signup_disabled`) instead of collapsing every error into a generic `?error=user_creation_failed`. Matches the behavior of the core OAuth callback and generic-oauth. The `user_creation_failed` value is still used as a fallback when `result.data` is missing without an explicit error.
|
||||
@@ -257,11 +257,21 @@ export const oAuthProxy = <O extends OAuthProxyOptions>(opts?: O) => {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
if (result.error || !result.data) {
|
||||
if (result.error) {
|
||||
ctx.context.logger.error(
|
||||
"Failed to create user or session",
|
||||
"OAuth proxy callback error",
|
||||
result.error,
|
||||
);
|
||||
throw redirectOnError(
|
||||
ctx,
|
||||
errorURL,
|
||||
result.error.split(" ").join("_"),
|
||||
);
|
||||
}
|
||||
if (!result.data) {
|
||||
ctx.context.logger.error(
|
||||
"OAuth proxy callback missing session data",
|
||||
);
|
||||
throw redirectOnError(ctx, errorURL, "user_creation_failed");
|
||||
}
|
||||
|
||||
|
||||
@@ -615,6 +615,79 @@ describe("oauth-proxy", async () => {
|
||||
expect(previewSessions.length).toBe(1);
|
||||
});
|
||||
|
||||
it("should forward result.error verbatim instead of collapsing to user_creation_failed", async () => {
|
||||
const production = await getTestInstance(
|
||||
{
|
||||
plugins: [
|
||||
oAuthProxy({
|
||||
currentURL: "http://preview.example.com",
|
||||
}),
|
||||
],
|
||||
socialProviders: {
|
||||
google: {
|
||||
clientId: "test",
|
||||
clientSecret: "test",
|
||||
disableSignUp: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ disableTestUser: true },
|
||||
);
|
||||
|
||||
const preview = await getTestInstance(
|
||||
{
|
||||
baseURL: "http://preview.example.com",
|
||||
plugins: [oAuthProxy()],
|
||||
socialProviders: {
|
||||
google: {
|
||||
clientId: "test",
|
||||
clientSecret: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ disableTestUser: true },
|
||||
);
|
||||
|
||||
const res = await production.client.signIn.social(
|
||||
{
|
||||
provider: "google",
|
||||
callbackURL: "/dashboard",
|
||||
},
|
||||
{ throw: true },
|
||||
);
|
||||
const state = new URL(res.url!).searchParams.get("state");
|
||||
|
||||
let encryptedProfile: string | null = null;
|
||||
let callbackURL: string | null = null;
|
||||
await production.client.$fetch(
|
||||
`/callback/google?code=test&state=${state}`,
|
||||
{
|
||||
onError(context) {
|
||||
const location = context.response.headers.get("location");
|
||||
if (location && location.includes("profile=")) {
|
||||
const url = new URL(location);
|
||||
encryptedProfile = url.searchParams.get("profile");
|
||||
callbackURL = url.searchParams.get("callbackURL");
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(encryptedProfile).toBeTruthy();
|
||||
|
||||
let proxyRedirect: string | null = null;
|
||||
await preview.client.$fetch(
|
||||
`/oauth-proxy-callback?callbackURL=${encodeURIComponent(callbackURL!)}&profile=${encodeURIComponent(encryptedProfile!)}`,
|
||||
{
|
||||
onError(context) {
|
||||
proxyRedirect = context.response.headers.get("location");
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(proxyRedirect).toBeTruthy();
|
||||
const url = new URL(proxyRedirect!);
|
||||
expect(url.searchParams.get("error")).toBe("signup_disabled");
|
||||
});
|
||||
|
||||
it("should reject expired profile payloads", async () => {
|
||||
const { client, auth } = await getTestInstance({
|
||||
plugins: [
|
||||
|
||||
Reference in New Issue
Block a user