diff --git a/docs/content/docs/plugins/magic-link.mdx b/docs/content/docs/plugins/magic-link.mdx index 91bcd94418..788bf36f79 100644 --- a/docs/content/docs/plugins/magic-link.mdx +++ b/docs/content/docs/plugins/magic-link.mdx @@ -3,9 +3,10 @@ title: Magic link description: Magic link plugin --- -Magic link or email link is a way to authenticate users without a password. When a user enters their email, a link is sent to their email. When the user clicks on the link, they are authenticated. +Magic link or email link is a way to authenticate users without a password. When a user enters their email, a link is sent to their email. When the user clicks on the link, they are authenticated. ## Installation + ### Add the server Plugin @@ -43,18 +44,19 @@ Magic link or email link is a way to authenticate users without a password. When }); ``` + ## Usage ### Sign In with Magic Link -To sign in with a magic link, you need to call `signIn.magicLink` with the user's email address. The `sendMagicLink` function is called to send the magic link to the user's email. +To sign in with a magic link, you need to call `signIn.magicLink` with the user's email address. The `sendMagicLink` function is called to send the magic link to the user's email. ```ts title="magic-link.ts" const { data, error } = await authClient.signIn.magicLink({ - email: "user@email.com", - callbackURL: "/dashboard" //redirect after successful login (optional) + email: "user@email.com", + callbackURL: "/dashboard", //redirect after successful login (optional) }); ``` @@ -65,16 +67,16 @@ If the user has not signed up, unless `disableSignUp` is set to `true`, the user When you send the URL generated by the `sendMagicLink` function to a user, clicking the link will authenticate them and redirect them to the `callbackURL` specified in the `signIn.magicLink` function. If an error occurs, the user will be redirected to the `callbackURL` with an error query parameter. -If no `callbackURL` is provided, the user will be redirected to the root URL. + If no `callbackURL` is provided, the user will be redirected to the root URL. If you want to handle the verification manually, (e.g, if you send the user a different url), you can use the `verify` function. ```ts title="magic-link.ts" const { data, error } = await authClient.magicLink.verify({ - query: { - token - } + query: { + token, + }, }); ``` @@ -91,3 +93,13 @@ and a `request` object as the second parameter. **expiresIn**: specifies the time in seconds after which the magic link will expire. The default value is `300` seconds (5 minutes). **disableSignUp**: If set to `true`, the user will not be able to sign up using the magic link. The default value is `false`. + +**generateToken**: The `generateToken` function is called to generate a token which is used to uniquely identify the user. The default value is a random string. There is one parameter: + +- `email`: The email address of the user. + + + When using `generateToken`, ensure that the returned string is hard to guess + because it is used to verify who someone actually is in a confidential way. By + default, we return a long and cryptographically secure string. + diff --git a/packages/better-auth/src/plugins/magic-link/index.ts b/packages/better-auth/src/plugins/magic-link/index.ts index e13a0bb980..bd2882c1ea 100644 --- a/packages/better-auth/src/plugins/magic-link/index.ts +++ b/packages/better-auth/src/plugins/magic-link/index.ts @@ -42,6 +42,10 @@ interface MagicLinkOptions { window: number; max: number; }; + /** + * Custom function to generate a token + */ + generateToken?: (email: string) => Promise | string; } export const magicLink = (options: MagicLinkOptions) => { @@ -108,7 +112,9 @@ export const magicLink = (options: MagicLinkOptions) => { } } - const verificationToken = generateRandomString(32, "a-z", "A-Z"); + const verificationToken = options?.generateToken + ? await options.generateToken(email) + : generateRandomString(32, "a-z", "A-Z"); await ctx.context.internalAdapter.createVerificationValue({ identifier: verificationToken, value: JSON.stringify({ email, name: ctx.body.name }), diff --git a/packages/better-auth/src/plugins/magic-link/magic-link.test.ts b/packages/better-auth/src/plugins/magic-link/magic-link.test.ts index 1a3f879600..de7e3e62ea 100644 --- a/packages/better-auth/src/plugins/magic-link/magic-link.test.ts +++ b/packages/better-auth/src/plugins/magic-link/magic-link.test.ts @@ -133,6 +133,36 @@ describe("magic link", async () => { emailVerified: true, }); }); + + it("should use custom generateToken function", async () => { + const customGenerateToken = vi.fn(() => "custom_token"); + + const { customFetchImpl } = await getTestInstance({ + plugins: [ + magicLink({ + async sendMagicLink(data) { + verificationEmail = data; + }, + generateToken: customGenerateToken, + }), + ], + }); + + const customClient = createAuthClient({ + plugins: [magicLinkClient()], + fetchOptions: { + customFetchImpl, + }, + baseURL: "http://localhost:3000/api/auth", + }); + + await customClient.signIn.magicLink({ + email: testUser.email, + }); + + expect(customGenerateToken).toHaveBeenCalled(); + expect(verificationEmail.token).toBe("custom_token"); + }); }); describe("magic link verify", async () => {