# 魔法链接 (/docs/plugins/magic-link)

魔法链接插件



魔法链接或电子邮件链接是一种无需密码即可验证用户身份的方法。当用户输入其电子邮件时，会向其邮箱发送一个链接。用户点击该链接后，即完成身份验证。

## 安装 [#安装]

<Steps>
  <Step>
    ### 添加服务器插件 [#添加服务器插件]

    将魔法链接插件添加到服务器：

    ```ts title="server.ts"
    import { betterAuth } from "better-auth";
    import { magicLink } from "better-auth/plugins";  // [!code highlight]

    export const auth = betterAuth({
        plugins: [
            magicLink({ // [!code highlight]
                sendMagicLink: async ({ email, token, url, metadata }, ctx) => { // [!code highlight]
                    // 向用户发送邮件 // [!code highlight]
                } // [!code highlight]
            }) // [!code highlight]
        ]
    })
    ```
  </Step>

  <Step>
    ### 添加客户端插件 [#添加客户端插件]

    将魔法链接插件添加到客户端：

    ```ts title="auth-client.ts"
    import { createAuthClient } from "better-auth/client";
    import { magicLinkClient } from "better-auth/client/plugins"; // [!code highlight]

    export const authClient = createAuthClient({
        plugins: [
            magicLinkClient() // [!code highlight]
        ]
    });
    ```
  </Step>
</Steps>

## 使用方法 [#使用方法]

### 使用魔法链接登录 [#使用魔法链接登录]

**Endpoint:** `POST /sign-in/magic-link`

### Client Side

```ts
const { data, error } = await authClient.signIn.magicLink({
    email: "user@email.com", // required, 发送魔法链接的电子邮件地址。
    name: "my-name", // 用户显示名称。仅在用户首次注册时使用。
    callbackURL: "/dashboard", // 验证后重定向的 URL。
    newUserCallbackURL: "/welcome", // URL to redirect after new user signup.
    errorCallbackURL: "/error", // URL to redirect if an error happens on verification If only callbackURL is provided but without an `errorCallbackURL` then they will be redirected to the callbackURL with an `error` query parameter.
    metadata: { inviteId: "123" }, // 传递给 sendMagicLink 回调的附加元数据。
});
```

### Server Side

```ts
const data = await auth.api.signInMagicLink({
    body: {
        email: "user@email.com", // required, 发送魔法链接的电子邮件地址。
        name: "my-name", // 用户显示名称。仅在用户首次注册时使用。
        callbackURL: "/dashboard", // 验证后重定向的 URL。
        newUserCallbackURL: "/welcome", // URL to redirect after new user signup.
        errorCallbackURL: "/error", // URL to redirect if an error happens on verification If only callbackURL is provided but without an `errorCallbackURL` then they will be redirected to the callbackURL with an `error` query parameter.
        metadata: { inviteId: "123" }, // 传递给 sendMagicLink 回调的附加元数据。
    },
    // Pass the current request headers so Better Auth can read and set cookies.
    headers: await headers(),
});
```

### Type Definition

```ts
type signInMagicLink = {
    /**
     * 发送魔法链接的电子邮件地址。 
     */
    email: string = "user@email.com"
    /**
     * 用户显示名称。仅在用户首次注册时使用。 
     */
    name?: string = "my-name"
    /**
     * 验证后重定向的 URL。 
     */
    callbackURL?: string = "/dashboard"
    /**
     * URL to redirect after new user signup.
     */
    newUserCallbackURL?: string = "/welcome"
    /**
     * URL to redirect if an error happens on verification
     * If only callbackURL is provided but without an `errorCallbackURL` then they will be
     * redirected to the callbackURL with an `error` query parameter.
     */
    errorCallbackURL?: string = "/error"
    /**
     * 传递给 sendMagicLink 回调的附加元数据。
     */
    metadata?: Record<string, any> = { inviteId: "123" }
}
```

<Callout>
  如果用户尚未注册，除非将 `disableSignUp` 设置为 `true`，否则用户将自动注册。
</Callout>

### 验证魔法链接 [#验证魔法链接]

当你将 `sendMagicLink` 函数生成的 URL 发送给用户时，用户点击链接后将完成身份验证，并重定向到 `signIn.magicLink` 函数中指定的 `callbackURL`。如果发生错误，用户会被重定向到带有错误查询参数的 `callbackURL`。

<Callout type="warn">
  如果未提供 `callbackURL`，用户将被重定向到根网址。
</Callout>

<Callout>
  当链接验证了一个已存在的账户，而该账户的邮箱从未被确认时，该账户上现有的密码将被移除，其会话也会被撤销。用户会通过该链接登录，并可通过密码重置设置新密码。这将以链接所证明的邮箱所有权作为账户的事实依据。
</Callout>

如果你想手动处理验证（例如，如果你向用户发送了不同的 URL），可以使用 `verify` 函数。

**Endpoint:** `GET /magic-link/verify`

### Client Side

```ts
const { data, error } = await authClient.magicLink.verify({
    query: {
        token: "123456", // required, 验证令牌。
        callbackURL: "/dashboard", // 验证后重定向的 URL，如果未提供则返回会话。
        newUserCallbackURL: "/welcome", // URL to redirect after new user signup.
        errorCallbackURL: "/error", // URL to redirect if an error happens on verification. If only callbackURL is provided but without an `errorCallbackURL` then they will be redirected to the callbackURL with an `error` query parameter.
    },
});
```

### Server Side

```ts
const data = await auth.api.magicLinkVerify({
    query: {
        token: "123456", // required, 验证令牌。
        callbackURL: "/dashboard", // 验证后重定向的 URL，如果未提供则返回会话。
        newUserCallbackURL: "/welcome", // URL to redirect after new user signup.
        errorCallbackURL: "/error", // URL to redirect if an error happens on verification. If only callbackURL is provided but without an `errorCallbackURL` then they will be redirected to the callbackURL with an `error` query parameter.
    },
    // Pass the current request headers so Better Auth can read and set cookies.
    headers: await headers(),
});
```

### Type Definition

```ts
type magicLinkVerify = {
    /**
     * 验证令牌。 
     */
    token: string = "123456"
    /**
     * 验证后重定向的 URL，如果未提供则返回会话。 
     */
    callbackURL?: string = "/dashboard"
    /**
     * URL to redirect after new user signup.
     */
    newUserCallbackURL?: string = "/welcome"
    /**
     * URL to redirect if an error happens on verification.
     * If only callbackURL is provided but without an `errorCallbackURL` then they will be
     * redirected to the callbackURL with an `error` query parameter.
     */
    errorCallbackURL?: string = "/error"
}
```

## 配置选项 [#配置选项]

**sendMagicLink**：当用户请求魔法链接时调用 `sendMagicLink` 函数。该函数接收一个包含以下属性的对象：

* `email`：用户的电子邮件地址。
* `url`：要发送给用户的 URL。此 URL 包含令牌。
* `token`：如果你想通过自定义 URL 发送令牌，则使用此令牌。
* `metadata`：从 `signIn.magicLink` 传递的附加请求元数据。

此外，函数的第二个参数为上下文对象 `ctx`。

**expiresIn**：指定魔法链接过期的时间（以秒为单位）。默认值为 `300` 秒（5 分钟）。

**allowedAttempts**（已弃用）：每次验证调用现在都会在首次尝试时以原子方式消耗令牌，因此无论此设置如何，重试都会始终失败并返回 `?error=INVALID_TOKEN`。保留此选项仅为保持源码兼容性，但会被忽略；不再支持多次尝试兑换。将其设置为除 `1` 之外的任何值都会在启动时发出 `console.warn`（包括 `0`，它以前会立即拒绝，而现在没有任何效果）。

**disableSignUp**：如果设置为 `true`，用户将无法使用魔法链接进行注册。默认值为 `false`。

**rateLimit**：`/sign-in/magic-link` 和 `/magic-link/verify` 端点的速率限制。默认值为 `{ window: 60, max: 5 }`。详情请参阅[速率限制](/docs/concepts/rate-limit)。

**generateToken**：`generateToken` 函数用于生成唯一标识用户的令牌。默认值为随机字符串。函数接收一个参数：

* `email`：用户的电子邮件地址。

<Callout type="warn">
  使用 `generateToken` 时，请确保返回的字符串难以猜测，因为它用于以保密方式验证用户身份。默认情况下，我们返回一个长度较长且密码学安全的字符串。
</Callout>

**storeToken**：控制 Better Auth 在创建验证标识符之前如何转换魔法链接令牌。默认值为 `"plain"`。

`storeToken` 可为以下值之一：

* `"plain"`：Better Auth 创建验证标识符前不转换令牌。
* `"hashed"`：创建标识符前使用默认哈希器对令牌进行哈希处理。
* `{ type: "custom-hasher", hash: (token: string) => Promise<string> }`：创建标识符前使用自定义哈希器转换令牌。

Better Auth 会在转换后的令牌前添加 `magic-link:`，然后应用全局 [`verification.storeIdentifier`](/docs/reference/options#verification) 设置。链接中发送的令牌值以及传给 `sendMagicLink` 的值均不会改变。自定义 `storeToken` 哈希器接收原始令牌，无需添加此前缀。

存储后端本身由全局 [`verification`](/docs/reference/options#verification) 配置控制。如果你配置了 `secondaryStorage`，魔法链接验证记录可以存储在那里，而不是数据库中。

升级到带用途前缀的验证标识符后，请为升级前发出的魔法链接重新申请链接。新版本无法兑换旧链接，并会返回 `INVALID_TOKEN`。现有用户和账号无需迁移数据。请同时升级所有共用验证存储的服务器，以确保它们对哪些链接可兑换的判断一致。

<Callout type="warn">
  当 `secondaryStorage` 为验证提供支持时（`verification.storeInDatabase: false`），原子单次使用保证要求你的二级存储公开 `getAndDelete`（Redis `GETDEL`、KV `getAndDelete`）。Better Auth 不会在验证令牌消耗时回退到单独的 `get` 和 `delete` 操作。使用二级存储验证的多实例部署必须配置实现了 `getAndDelete` 的后端。
</Callout>

