mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-25 08:31:37 -05:00
feat: custom generate token function for magic links (#1362)
This commit is contained in:
@@ -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
|
||||
|
||||
<Steps>
|
||||
<Step>
|
||||
### Add the server Plugin
|
||||
@@ -43,18 +44,19 @@ Magic link or email link is a way to authenticate users without a password. When
|
||||
});
|
||||
```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## 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.
|
||||
|
||||
<Callout type="warn">
|
||||
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.
|
||||
</Callout>
|
||||
|
||||
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.
|
||||
|
||||
<Callout type="warn">
|
||||
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.
|
||||
</Callout>
|
||||
|
||||
@@ -42,6 +42,10 @@ interface MagicLinkOptions {
|
||||
window: number;
|
||||
max: number;
|
||||
};
|
||||
/**
|
||||
* Custom function to generate a token
|
||||
*/
|
||||
generateToken?: (email: string) => Promise<string> | 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 }),
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user