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 () => {