# One Tap (/docs/plugins/one-tap)

Better Auth 的 One Tap 插件



一键登录插件允许用户通过 Google 的 One Tap API 只需轻点一次即可登录。该插件为您提供一个简单的方式将 One Tap 集成到您的应用中，负责处理客户端和服务器端的逻辑。

## 安装 [#安装]

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

    将 One Tap 插件添加到您的认证配置中：

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

    export const auth = betterAuth({
        plugins: [
            oneTap({
              clientId: process.env.GOOGLE_CLIENT_ID as string, // [!code highlight]
            }),
        ]
    });
    ```
  </Step>

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

    添加客户端插件并指定用户登录后应重定向到的位置，以及是否需要额外验证（如双因素认证）。

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

    export const authClient = createAuthClient({
      plugins: [
        oneTapClient({
          clientId: "YOUR_CLIENT_ID",
          // 可选的客户端配置：
          autoSelect: false,
          cancelOnTapOutside: true,
          context: "signin",
          additionalOptions: {
            // Google 初始化方法的任何额外选项
          },
          // 配置提示行为和指数退避：
          promptOptions: {
            baseDelay: 1000,   // 重试延迟基数（毫秒，默认：1000）
            maxAttempts: 5     // 触发 onPromptNotification 前的最大尝试次数（默认：5）
          }
        })
      ]
    });
    ```
  </Step>
</Steps>

### 用法 [#用法]

#### 提示模式（默认） [#提示模式默认]

要显示 One Tap 弹窗，只需在您的认证客户端调用 oneTap 方法：

```ts
import { authClient } from "@/lib/auth-client";

await authClient.oneTap(); // [!code highlight]
```

#### 按钮模式 [#按钮模式]

如果想展示“使用 Google 登录”按钮而不是自动弹出提示，可使用 `button` 选项：

<Tabs items="[&#x22;Vanilla JS&#x22;, &#x22;React Ref&#x22;]">
  <Tab value="Vanilla JS">
    ```tsx
    // 在您的组件内
    <div id="google-signin-button"></div>

    // 使用按钮配置调用 oneTap
    await authClient.oneTap({
      button: {
        container: "#google-signin-button", // CSS 选择器或 HTMLElement
        config: {
          theme: "outline",
          size: "large",
          type: "standard",
          text: "signin_with"
        }
      }
    });
    ```
  </Tab>

  <Tab value="React Ref">
    ```tsx
    import { useEffect, useRef } from "react";

    function SignInButton() {
      const buttonRef = useRef<HTMLDivElement>(null);

      useEffect(() => {
        if (buttonRef.current) {
          authClient.oneTap({
            button: {
              container: buttonRef.current,
              config: {
                theme: "filled_blue",
                size: "large"
              }
            }
          });
        }
      }, []);

      return <div ref={buttonRef}></div>;
    }
    ```
  </Tab>
</Tabs>

### 自定义重定向行为 [#自定义重定向行为]

默认情况下，登录成功后插件会硬跳转到 `/`。您可以按以下方法自定义该行为：

#### 避免硬跳转 [#避免硬跳转]

传递带有 onSuccess 回调的 fetchOptions，用于在不重新加载页面的情况下处理登录响应：

```ts
import { authClient } from "@/lib/auth-client";

await authClient.oneTap({
  fetchOptions: {
    onSuccess: () => {
      // 例如，使用路由器导航而不完整重载页面：
      router.push("/dashboard");
    }
  }
});
```

#### 指定自定义回调 URL [#指定自定义回调-url]

如果登录后想进行硬跳转至不同页面，使用 callbackURL 选项：

```ts
import { authClient } from "@/lib/auth-client";

await authClient.oneTap({
  callbackURL: "/dashboard"
});
```

#### 使用指数退避处理提示被关闭 [#使用指数退避处理提示被关闭]

如果提示框关闭时用户尚未登录，插件会根据配置的 `promptOptions` 采用指数退避策略重试显示 One Tap 提示。应用通过 `google.accounts.id.cancel()` 关闭的提示框不会重试。

跳过的提示不会重试。Google 不会说明提示被跳过的原因；用户关闭提示后，Google 会进入冷却期，重试无法缩短该冷却时间。

插件会向 Google 的 `prompt()` 传入监听器来跟踪这些状态，因此 Google Identity Services 会记录一条关于提示 UI 状态方法的 FedCM 迁移警告。只要传入任何监听器，Google 都会记录该警告，即使监听器没有调用这些方法。在 FedCM 模式下，插件只会调用 FedCM 支持的方法，因此可以忽略此警告。

提示被跳过，或达到最大尝试次数仍未成功登录时，可通过 `onPromptNotification` 回调接收通知，并渲染备用界面（例如传统的 Google 登录按钮），让用户手动重新开始流程：

```ts
import { authClient } from "@/lib/auth-client";

await authClient.oneTap({
  onPromptNotification: (notification) => {
    console.warn("提示已被关闭或跳过。请考虑显示替代的登录选项。", notification);
    // 在这里渲染您的替代 UI
  }
});
```

### 客户端选项 [#客户端选项]

* `clientId`：您的 Google One Tap API 的客户端 ID。
* `autoSelect`：如果用户已登录，自动选择账户。默认为 false。
* `cancelOnTapOutside`：当用户点击弹窗外部时取消 One Tap 弹窗。请注意，当 FedCM 处于活动状态时，此选项可能无效，因为浏览器会管理取消行为。默认为 true。
* `uxMode`：Google One Tap 流程使用的模式。可以是 "popup" 或 "redirect"。默认为 "popup"。
* `context`：应使用 One Tap API 的上下文。可以是 "signin"、"signup" 或 "use"。默认为 "signin"。
* `additionalOptions`：额外的选项，按照 [Google Identity Services 文档](https://developers.google.com/identity/gsi/web/reference/js-reference#google.accounts.id.prompt) 传递给 Google 的初始化方法。
* `promptOptions`：提示行为和指数退避的配置：
  * `baseDelay`：重试延迟基数（毫秒）。默认是 1000。
  * `maxAttempts`：触发 onPromptNotification 回调前的最大尝试次数。默认是 5。
  * `fedCM`：启用时，调用 [`navigator.credentials.preventSilentAccess()`](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/preventSilentAccess) 以在登出时清除浏览器的 FedCM 凭据状态。FedCM 本身由 Google Identity Services 库管理，无法禁用。默认是 true。

### 按钮模式选项 [#按钮模式选项]

使用按钮模式时，向 `oneTap` 方法传递 `button` 对象，包含以下属性：

* `container`：渲染按钮的 HTML 元素或 CSS 选择器（必需）
* `config`：按钮配置选项（可选）：
  * `type`：按钮类型——`"standard"`（默认）或 `"icon"`（仅图标）
  * `theme`：按钮主题——`"outline"`（默认）、`"filled_blue"` 或 `"filled_black"`
  * `size`：按钮大小——`"large"`（默认）、`"medium"` 或 `"small"`
  * `text`：按钮文本——`"signin_with"`（默认）、`"signup_with"`、`"continue_with"` 或 `"signin"`
  * `shape`：按钮形状——`"rectangular"`（默认）、`"pill"`、`"circle"` 或 `"square"`
  * `logo_alignment`：Google Logo 对齐方式——`"left"`（默认）或 `"center"`（仅标准按钮）
  * `width`：最小按钮宽度（像素，最大 400）
  * `locale`：使用指定的语言代码显示按钮（例如 `"zh_CN"`）

### 服务器端选项 [#服务器端选项]

* `disableSignUp`：禁用注册选项，仅允许现有用户登录。默认为 false。
* `clientId`：用于验证 ID 令牌受众的 Google 客户端 ID。除非配置了 `socialProviders.google.clientId`，否则为必需项。

<Callout type="info">
  **限制为 Google Workspace 域（`hd`）**

  One Tap 回调会应用 `socialProviders.google.hd` 中配置的受托管域限制，与标准的 Google 登录流程一致：
  当 `hd` 设置为某个域时，如果令牌的 `hd` 声明缺失或不匹配，则会被拒绝。将 `hd` 设为 `"*"` 可允许任意 Google Workspace 托管域，同时仍会拒绝没有 `hd` 声明的令牌。

  该值从 `socialProviders.google` 中读取，因此只有在您配置了 Google 提供程序时，限制才会生效。如果您只使用 `oneTap({ clientId })` 而不配置 `socialProviders.google`，则不会应用受托管域限制。若要将 One Tap 登录限制为某个 Workspace 域，请在 `socialProviders.google` 中配置 `hd`。
</Callout>

<Callout type="info">
  **将 Google 资料映射到用户**

  配置 `socialProviders.google` 时，One Tap 会像标准 Google 登录一样，使用其 `mapProfileToUser` 或 `getUserInfo` 构建用户。你映射的额外字段（例如来自 `given_name` 的名字）只有在 `user.additionalFields` 中声明且未标记为 `input: false` 时才会保存。账户始终以经过验证的令牌 `sub` 为键。

  启用 `overrideUserInfoOnSignIn` 后，已有用户每次通过 One Tap 登录时都会根据映射后的资料更新。

  如果只配置 `oneTap({ clientId })`，而未配置 `socialProviders.google`，One Tap 会使用令牌中的 `name`、`email`、`email_verified` 和 `picture` 声明。
</Callout>

### 已授权的 JavaScript 来源 [#已授权的-javascript-来源]

请确保为您的客户端 ID 配置已授权的 JavaScript 来源（例如 [http://localhost:3000](http://localhost:3000)、[https://example.com](https://example.com)）。这是 Google One Tap API 的必需步骤，除非正确设置了来源，否则无法正常工作。

