Skip to content

Commit 69b88f2

Browse files
martinrivieregithub-actions[bot]
authored andcommitted
fix(magic-link): handle query params in errorCallbackUrl (#6383)
1 parent 28cd9d1 commit 69b88f2

File tree

2 files changed

+42
-17
lines changed

2 files changed

+42
-17
lines changed

packages/better-auth/src/plugins/magic-link/index.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -315,31 +315,32 @@ export const magicLink = (options: MagicLinkOptions) => {
315315
? decodeURIComponent(ctx.query.errorCallbackURL)
316316
: callbackURL,
317317
ctx.context.baseURL,
318-
).toString();
318+
);
319+
320+
function redirectWithError(error: string): never {
321+
errorCallbackURL.searchParams.set("error", error);
322+
throw ctx.redirect(errorCallbackURL.toString());
323+
}
324+
319325
const newUserCallbackURL = new URL(
320326
ctx.query.newUserCallbackURL
321327
? decodeURIComponent(ctx.query.newUserCallbackURL)
322328
: callbackURL,
323329
ctx.context.baseURL,
324330
).toString();
325-
const toRedirectTo = callbackURL?.startsWith("http")
326-
? callbackURL
327-
: callbackURL
328-
? `${ctx.context.options.baseURL}${callbackURL}`
329-
: ctx.context.options.baseURL;
330331
const storedToken = await storeToken(ctx, token);
331332
const tokenValue =
332333
await ctx.context.internalAdapter.findVerificationValue(
333334
storedToken,
334335
);
335336
if (!tokenValue) {
336-
throw ctx.redirect(`${errorCallbackURL}?error=INVALID_TOKEN`);
337+
redirectWithError("INVALID_TOKEN");
337338
}
338339
if (tokenValue.expiresAt < new Date()) {
339340
await ctx.context.internalAdapter.deleteVerificationValue(
340341
tokenValue.id,
341342
);
342-
throw ctx.redirect(`${errorCallbackURL}?error=EXPIRED_TOKEN`);
343+
redirectWithError("EXPIRED_TOKEN");
343344
}
344345
await ctx.context.internalAdapter.deleteVerificationValue(
345346
tokenValue.id,
@@ -363,14 +364,10 @@ export const magicLink = (options: MagicLinkOptions) => {
363364
isNewUser = true;
364365
user = newUser;
365366
if (!user) {
366-
throw ctx.redirect(
367-
`${errorCallbackURL}?error=failed_to_create_user`,
368-
);
367+
redirectWithError("failed_to_create_user");
369368
}
370369
} else {
371-
throw ctx.redirect(
372-
`${errorCallbackURL}?error=new_user_signup_disabled`,
373-
);
370+
redirectWithError("new_user_signup_disabled");
374371
}
375372
}
376373

@@ -385,9 +382,7 @@ export const magicLink = (options: MagicLinkOptions) => {
385382
);
386383

387384
if (!session) {
388-
throw ctx.redirect(
389-
`${errorCallbackURL}?error=failed_to_create_session`,
390-
);
385+
redirectWithError("failed_to_create_session");
391386
}
392387

393388
await setSessionCookie(ctx, {

packages/better-auth/src/plugins/magic-link/magic-link.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,36 @@ describe("magic link", async () => {
103103
);
104104
});
105105

106+
it("should redirect to errorCallbackURL in case of error", async () => {
107+
const errorCallbackURL = new URL("http://localhost:3000/error-page");
108+
errorCallbackURL.searchParams.set("foo", "bar");
109+
errorCallbackURL.searchParams.set("baz", "qux");
110+
111+
await client.magicLink.verify(
112+
{
113+
query: {
114+
token: "invalid-token",
115+
errorCallbackURL: errorCallbackURL.toString(),
116+
},
117+
},
118+
{
119+
onError(context) {
120+
expect(context.response.status).toBe(302);
121+
122+
const location = context.response.headers.get("location");
123+
expect(location).toBeDefined();
124+
125+
const url = new URL(location!);
126+
expect(url.origin).toBe(errorCallbackURL.origin);
127+
expect(url.pathname).toBe(errorCallbackURL.pathname);
128+
expect(url.searchParams.get("foo")).toBe("bar");
129+
expect(url.searchParams.get("baz")).toBe("qux");
130+
expect(url.searchParams.get("error")).toBe("INVALID_TOKEN");
131+
},
132+
},
133+
);
134+
});
135+
106136
it("should sign up with magic link", async () => {
107137
const email = "new-email@email.com";
108138
await client.signIn.magicLink({

0 commit comments

Comments
 (0)