feat: custom generate token function for magic links (#1362)

This commit is contained in:
Ted
2025-02-14 05:50:36 +00:00
committed by GitHub
parent 0ea9c29cc8
commit b3371272fc
3 changed files with 57 additions and 9 deletions

View File

@@ -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>

View File

@@ -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 }),

View File

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