# OAuth (/docs/concepts/oauth)

了解如何配置社交 OAuth 提供商、登录和关联账户、请求权限范围、传递额外数据、刷新访问令牌、映射用户资料以及自定义提供商选项。



Better Auth 内置支持 OAuth 2.0 和 OpenID Connect。这使你可以通过流行的 OAuth 提供商（如 Google、Facebook、GitHub 等）进行用户身份验证。

如果你想使用的提供商没有直接支持，可以使用 [Generic OAuth 插件](/docs/plugins/generic-oauth) 来实现自定义集成。

## 配置社交提供商 [#配置社交提供商]

要启用社交提供商，需要为该提供商提供 `clientId` 和 `clientSecret`。

下面是配置 Google 作为提供商的示例：

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
    },
  },
});
```

## 用法 [#用法]

### 登录 [#登录]

要使用社交提供商登录，可以调用 `signIn.social` 函数（客户端）或 `auth.api`（服务器端）。

```ts
// 客户端用法
await authClient.signIn.social({
  provider: "google", // 或其它提供商 id
})
```

```ts
// 服务器端用法
await auth.api.signInSocial({
  body: {
    provider: "google", // 或其它提供商 id
  },
});
```

### 关联账户 [#关联账户]

要将账户关联到社交提供商，可以调用 `linkAccount` 函数（客户端）或 `auth.api`（服务器端）。

```ts
await authClient.linkSocial({
  provider: "google", // 或其它提供商 id
})
```

服务器端用法：

```ts
await auth.api.linkSocialAccount({
  body: {
    provider: "google", // 或其它提供商 id
  },
  headers: await headers() // 包含用户会话令牌的请求头
});
```

### 验证 OAuth 用户信息 [#验证-oauth-用户信息]

使用 [`user.validateUserInfo`](/docs/concepts/users-accounts#回调函数) 在 Better Auth 创建用户、关联新账户或让现有用户重新登录之前拒绝 OAuth 身份。回调会接收映射后的 `user`，以及 `source.oauth` 中的提供商 id 和原始提供商资料。

当 OAuth 身份首次创建时（来自常规回调、ID token 登录、One Tap 或 OAuth Proxy 的 `create-user`），关联新账户时（`link-account`），以及现有 OAuth 用户每次登录时（`sign-in`），都会运行该回调。在 `sign-in` 操作中，`user` 携带的是提供商的*最新*邮箱，因此，如果用户的提供商邮箱后来变更为不允许的域名，域名检查会拒绝该用户。它适用于无状态配置，因为无论是否使用数据库，都会在相同的时间点运行。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  user: {
    validateUserInfo: ({ user, source }) => {
      if (source.oauth?.providerId !== "google") return;

      if (!user.email?.endsWith("@example.com")) {
        return {
          error: "email_not_allowed",
          errorDescription: "Use your example.com email to sign in",
        };
      }
    },
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
  },
});
```

### 获取访问令牌 [#获取访问令牌]

使用 `getAccessToken` 获取已关联账户的访问令牌。每个令牌或提供商资料请求都必须明确选择账户：将 Better Auth 账户记录的 `id` 作为 `accountId` 传入，或传入 `useAccountCookie: true`，从其签名 Cookie 中选择账户。`providerId` 永远不能作为账户选择器，不包含任何一种受支持选择器的请求均无效。

账户记录 ID 可从 `listAccounts` 获取。如果其访问令牌已过期，Better Auth 会在返回前刷新该令牌。

```ts
const { data: accounts, error } = await authClient.listAccounts();

if (error) {
  throw new Error(error.message);
}

const account = accounts?.find((account) => account.providerId === "google");

if (!account) {
  throw new Error("Google account is not linked");
}

const { accessToken } = await authClient.getAccessToken({
  accountId: account.id,
})
```

启用 [`account.storeAccountCookie`](/docs/reference/options#storeaccountcookie) 后，显式从签名 Cookie 中选择账户：

```ts
const { accessToken } = await authClient.getAccessToken({
  useAccountCookie: true,
});
```

在服务器端使用时，传入相同的账户记录 ID。如果不包含会话请求头，受信任的服务器调用也可以提供 `userId`。

```ts
await auth.api.getAccessToken({
  body: {
    accountId: account.id,
  },
  headers: await headers(), // 包含用户会话令牌的请求头
});
```

### 刷新访问令牌 [#刷新访问令牌]

当你需要立即刷新所选账户的访问令牌时，使用 `refreshToken`。它使用与 `getAccessToken` 相同的显式选择器约定。

```ts
const tokens = await authClient.refreshToken({
  accountId: account.id,
});
```

要改用签名账户 Cookie：

```ts
await auth.api.refreshToken({
  body: {
    useAccountCookie: true,
  },
  headers: await headers(),
});
```

### 获取提供商提供的账户信息 [#获取提供商提供的账户信息]

使用 `accountInfo` 获取已关联账户在提供商处的当前资料数据。传入 Better Auth 账户记录的 `id`，而不是提供商的 subject 标识符。

响应会将身份数据与资料数据分开。`account` 包含所选的 Better Auth 记录及其提供商密钥，`user` 包含可变的资料字段，`data` 包含原始提供商响应。

```ts
const info = await authClient.accountInfo({
  query: { accountId: account.id },
});

console.log(info.data?.account.accountId);
console.log(info.data?.user.email);
```

当应通过签名账户 Cookie 选择账户时：

```ts
const info = await authClient.accountInfo({
  query: { useAccountCookie: true },
});
```

服务器端用法：

```ts
await auth.api.accountInfo({
  query: {
    accountId: account.id,
  },
  headers: await headers(), // 包含用户会话令牌的请求头
});
```

### 请求额外权限范围 [#请求额外权限范围]

有时你的应用可能需要用户在注册后再授予额外的 OAuth 权限范围（例如访问 GitHub 仓库或 Google Drive）。用户可能不想一开始就授予全部权限，更倾向于先授予最小权限，然后根据需要追加权限。

你可以调用相同提供商的 `linkSocial` 方法请求额外权限，这会触发新的 OAuth 流程，申请额外权限，同时保持现有的账户关联。

```ts
const requestAdditionalScopes = async () => {
    await authClient.linkSocial({
        provider: "google",
        scopes: ["https://www.googleapis.com/auth/drive.file"],
    });
};
```

<Callout>
  请确保你正在运行 Better Auth 1.2.7 或更高版本。较早版本（如 1.2.2）在尝试使用现有提供商关联获取额外权限时，可能会显示 “Social account already linked” 错误。
</Callout>

### 自定义授权 URL [#自定义授权-url]

要向提供商的授权端点传递额外查询参数，请在调用 `signIn.social` 或 `linkSocial` 时传入 `additionalParams`。这些值会在框架写入 OAuth state、PKCE challenge 和 `redirect_uri` 后应用；保留键 `state`、`client_id`、`redirect_uri`、`response_type`、`code_challenge`、`code_challenge_method` 和 `scope` 会被拒绝并返回 400，因此调用方无法破坏回调关联。

```ts
await authClient.signIn.social({
  provider: "cognito",
  additionalParams: {
    identity_provider: "Google", // 跳过 Cognito 托管 UI 选择器
  },
});

await authClient.linkSocial({
  provider: "google",
  loginHint: "user@example.com",
  additionalParams: {
    access_type: "offline",
    prompt: "consent",
  },
});
```

提供商自身内置的查询参数（例如 Google 的 `include_granted_scopes=true`、Facebook 的 `config_id`，以及通过 `identityProvider` 设置时 Cognito 的 `identity_provider`）会与调用时的 `additionalParams` 合并；发生键冲突时，以调用时的值为准。

### 通过 OAuth 流程传递额外数据 [#通过-oauth-流程传递额外数据]

Better Auth 允许你在 OAuth 流程中传递额外数据，但不将其存储到数据库。这适用于跟踪推荐码、分析来源或其他应在认证时处理但无需持久保存的临时数据。

发起 OAuth 登录或关联账户时，传递额外数据：

```ts
// 客户端：带额外数据登录
await authClient.signIn.social({
  provider: "google",
  additionalData: {
    referralCode: "ABC123",
    source: "landing-page",
  },
});

// 客户端：带额外数据关联账户
await authClient.linkSocial({
  provider: "google",
  additionalData: {
    referralCode: "ABC123",
  },
});

// 服务器端：带额外数据登录
await auth.api.signInSocial({
  body: {
    provider: "google",
    additionalData: {
      referralCode: "ABC123",
      source: "admin-panel",
    },
  },
});
```

#### 在钩子中访问额外数据 [#在钩子中访问额外数据]

额外数据可以通过 `getOAuthState` 在 OAuth 回调的钩子中访问。

<Callout>
  这通常适用于 `/callback/:id` 等 OAuth 回调路径。
</Callout>

使用 after 钩子的示例：

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { createAuthMiddleware, getOAuthState } from "better-auth/api";

export const auth = betterAuth({
  // 其他配置...
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      // Additional data is only available during OAuth callback
      if (ctx.path === "/callback/:id") {
        const additionalData = await getOAuthState<{
          referralCode?: string;
          source?: string;
        }>();

        if (additionalData) {
          // IMPORTANT: Validate and sanitize the data before using it
          // This data comes from the client and should not be trusted

          // Example: Validate and process referral code
          if (additionalData.referralCode) {
            const isValidFormat = /^[A-Z0-9]{6}$/.test(additionalData.referralCode);
            if (isValidFormat) {
              // Verify the referral code exists in your database
              const referral = await db.referrals.findByCode(additionalData.referralCode);
              if (referral) {
                // Safe to use the verified referral
                await db.referrals.incrementUsage(referral.id);
              }
            }
          }

          // Track analytics (low-risk usage)
          if (additionalData.source) {
            await analytics.track("oauth_signin", {
              source: additionalData.source,
              userId: ctx.context.session?.user.id,
            });
          }
        }
      }
    }),
  },
});
```

使用数据库钩子的示例：

```ts title="auth.ts"
 // 你也可以在数据库钩子中访问额外数据
  databaseHooks: {
    user: {
      create: {
        before: async (user, ctx) => {
          if (ctx.path === "/callback/:id") {
            const additionalData = await getOAuthState<{ referredFrom?: string }>();
            if (additionalData?.referredFrom) {
              return {
                data: {
                  referredFrom: additionalData.referredFrom,
                },
              };
            }
          }
        },
      },
    },
  },
```

<Callout>
  默认情况下，OAuth state 包含以下数据：

  * `callbackURL` - OAuth 流程的回调 URL
  * `codeVerifier` - OAuth 流程的代码验证器
  * `errorURL` - OAuth 流程的错误 URL
  * `newUserURL` - OAuth 流程的新用户 URL
  * `link` - OAuth 流程的关联信息（邮箱和用户 id）
  * `requestSignUp` - 是否请求注册 OAuth 流程
  * `expiresAt` - OAuth state 的过期时间
  * `serverContext` - 在服务器端设置且在重定向后仍保留的值（参见[传递服务器信任的数据](#passing-server-trusted-data)）
  * `[key: string]` - 你传入的 `additionalData`。这些数据来源于客户端，因此应视为不受信任。
</Callout>

#### 传递服务器信任的数据 [#传递服务器信任的数据]

`additionalData` 由客户端提供，因此必须在回调时验证后才能使用。当插件（或你自己的 `before` 钩子）需要在重定向过程中传递服务器派生的数据时，请使用 `addOAuthServerContext`。它会写入客户端无法填充的仅限服务器端的存储槽，这些值可在回调时通过 `serverContext` 读取。

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import {
  addOAuthServerContext,
  createAuthMiddleware,
  getOAuthState,
} from "better-auth/api";

export const auth = betterAuth({
  hooks: {
    before: createAuthMiddleware(async (ctx) => {
      // 社交和通用 OAuth 提供商都会在这里登录。
      if (ctx.path === "/sign-in/social") {
        // 在服务器端派生，因此在回调时可以安全信任。
        await addOAuthServerContext({ tenantId: ctx.context.tenantId });
      }
    }),
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path === "/callback/:id") {
        const tenantId = (await getOAuthState())?.serverContext?.tenantId;
        // 可安全使用，无需重新验证：客户端无法设置此值。
      }
    }),
  },
});
```

## 处理不提供邮箱的提供商 [#处理不提供邮箱的提供商]

Better Auth 目前要求每条用户记录都必须有电子邮件地址。大多数提供商会在使用 `email` scope 时返回邮箱，但有些提供商在合法情况下可能不会返回。当这种情况发生时，OAuth 流程会失败并报 `error=email_not_found`（而对于 Generic OAuth 插件则是 `error=email_is_missing`）。

下表总结了每个受影响的提供商：何时可能缺少 `email`、在 `mapProfileToUser` 中可作为回退的稳定标识符，以及对提供商 `email_verified` 信号的可信度。

| 提供商                | `email` 可能缺失的情况                                                                                                   | 稳定的回退 ID                           | 对 `email_verified` 的信任程度                                                                                              |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Apple              | 首次登录后的每次登录（Apple 只在首次同意时返回 `email`）                                                                               | `profile.sub`（在 Apple Team 内稳定）    | 可靠；中继地址也会标记为已验证                                                                                                       |
| Discord            | 仅手机号账户；未授予 `email` scope                                                                                          | `profile.id`（snowflake）            | 可靠（专用的 `verified` 字段）                                                                                                 |
| Facebook           | 即使已授予 `email` 权限，账户中也没有有效邮箱                                                                                       | `profile.id`（应用范围内）                | 未知：Graph API 不提供针对单个邮箱的验证标志                                                                                           |
| GitHub             | 用户将邮箱设为私密；GitHub App 缺少 “Email addresses” 权限                                                                      | `profile.id`（数字）                   | 可靠                                                                                                                    |
| LinkedIn           | 会员没有已确认的邮箱；未授予 `email` scope                                                                                      | `profile.sub`（每个应用成对）              | 存在时可靠                                                                                                                 |
| Microsoft Entra ID | 托管用户没有 `mail` 属性，除非将 `email` 配置为[可选声明](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims) | `profile.oid`（在 `profile.tid` 内稳定） | **不可信**：Microsoft [明确警告](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference)不要将其用于授权 |
| Roblox             | 默认 Roblox 资料流程不会返回邮箱；Better Auth 会根据 `profile.sub` 创建不可路由的占位邮箱                                                    | `profile.sub`（Roblox 用户 ID）        | 对默认资料流程未知                                                                                                             |

### 使用 `mapProfileToUser` 创建占位邮箱 [#使用-mapprofiletouser-创建占位邮箱]

当 `email` 字段为 null 或不存在时，回退到提供商的稳定 ID：

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    discord: {
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
      mapProfileToUser: (profile) => ({
        email: profile.email ?? `${profile.id}@discord.placeholder.invalid`,
      }),
    },
    apple: {
      clientId: process.env.APPLE_CLIENT_ID!,
      clientSecret: process.env.APPLE_CLIENT_SECRET!,
      mapProfileToUser: (profile) => ({
        email: profile.email ?? `${profile.sub}@apple.placeholder.invalid`,
      }),
    },
    microsoft: {
      clientId: process.env.MICROSOFT_CLIENT_ID!,
      clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
      mapProfileToUser: (profile) => ({
        email: profile.email ?? `${profile.oid}@entra.placeholder.invalid`,
      }),
    },
  },
});
```

<Callout type="warn">
  占位邮箱不是联系地址。会发送邮件的插件（密码重置、魔法链接、邮箱验证、组织邀请）无法向其发送邮件。请使用你控制的域名或保留域名 `.invalid`，以免误将邮件发送到真实邮箱。
</Callout>

### 提供商特定说明 [#提供商特定说明]

* **Apple**：第一次看到邮箱时请将其持久化保存。Apple 不提供用户信息端点，因此如果你在首次登录时不保存它，之后就无法再获取。`email_verified` 和 `is_private_email` 都会被序列化为 **字符串**（`"true"` / `"false"`），而不是布尔值。
* **GitHub**：默认会请求 `user:email` scope。私密邮箱在 `/user` 上仍会返回 `null`；主要已验证地址可在 [`/user/emails`](https://docs.github.com/en/rest/users/emails) 获取。
* **Microsoft Entra ID**：由于 `email` 可随租户变化且从不验证，请使用 `profile.oid`（不可变、在租户内稳定）作为身份锚点；仅将 `email` 视为资料属性。Microsoft 的[声明验证指南](https://learn.microsoft.com/en-us/entra/identity-platform/claims-validation)明确警告，切勿将 `email`、`preferred_username` 或 `unique_name` 用于授权决策。
* **Facebook**：由于没有逐邮箱验证标志，除非你自行执行验证挑战，否则应将每个 Facebook 邮箱都视为未验证。

Better Auth 通过稳定的 `(providerId, accountId)` 键识别返回的 OAuth 账户，但关联的 Better Auth 用户仍然需要电子邮件地址。对于不带邮箱的用户的支持进展，可在 [#9124](https://github.com/better-auth/better-auth/issues/9124) 中追踪。

## 提供商选项 [#提供商选项]

### clientId [#clientid]

由提供商签发的 OAuth 2.0 Client ID。

对于通过受众（audience）验证 ID token 的提供商（Google、Apple、Microsoft Entra、Facebook、Cognito），你可以传入一个数组，以接受为任意已配置客户端签发的 token。Better Auth 驱动授权码流程时会使用数组中的第一个条目；验证 ID token 的 `aud` 声明时会接受所有条目。这使你可以使用单一后端配置实现跨平台登录（Web、iOS、Android），其中每个平台的原生 SDK 都会使用各自的 Client ID 签发 token。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: [
        process.env.GOOGLE_WEB_CLIENT_ID as string,
        process.env.GOOGLE_IOS_CLIENT_ID as string,
        process.env.GOOGLE_ANDROID_CLIENT_ID as string,
      ],
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
  },
});
```

所有 Client ID 必须位于同一个提供商项目中，这样用户同意才能共享。对于不通过受众验证 ID token 的提供商，只接受单个字符串。

### scope [#scope]

请求的访问权限范围，例如 `email` 或 `profile`。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      scope: ["email", "profile"],
    },
  },
});
```

### redirectURI [#redirecturi]

提供商的自定义重定向 URI。默认使用 `/api/auth/callback/${providerName}`。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      redirectURI: "https://your-app.com/auth/callback",
    },
  },
});
```

### disableSignUp [#disablesignup]

禁用新用户注册。

### disableIdTokenSignIn [#disableidtokensignin]

禁用使用 ID token 登录。默认对某些提供商（如 Google 和 Apple）启用。

### verifyIdToken [#verifyidtoken]

用于验证 ID token 的自定义函数。接收 token、可选的 nonce，以及请求端点上下文，因此你可以根据请求头或其他请求数据进行分支处理。

<Callout type="warn">
  提供 `verifyIdToken` 会**替换**提供商内置的验证逻辑（签名、签发者、受众，以及 `exp`、`nbf` 和 `iat` 时间检查；时间检查允许 30 秒时钟偏差）。你的回调必须自行执行这些检查，包括时钟容差。来自客户端的请求头（例如 `x-platform`）由攻击者控制——只能用它们选择要验证的受众（或其他声明），不能将其本身视为身份凭证。
</Callout>

```ts title="auth.ts"
import { betterAuth } from "better-auth";
import { createRemoteJWKSet, jwtVerify } from "jose";

const appleJwks = createRemoteJWKSet(
  new URL("https://appleid.apple.com/auth/keys"),
);

export const auth = betterAuth({
  socialProviders: {
    apple: {
      clientId: "YOUR_APPLE_CLIENT_ID",
      clientSecret: "YOUR_APPLE_CLIENT_SECRET",
      verifyIdToken: async (token, nonce, ctx) => {
        // 从请求中选择受众，然后进行加密验证。
        const audience =
          ctx?.headers?.get("x-platform") === "ios"
            ? process.env.APPLE_APP_BUNDLE_IDENTIFIER!
            : process.env.APPLE_CLIENT_ID!;
        try {
          const { payload } = await jwtVerify(token, appleJwks, {
            issuer: "https://appleid.apple.com",
            audience,
            maxTokenAge: "1h",
            clockTolerance: 30,
          });
          if (nonce && payload.nonce !== nonce) {
            return false;
          }
          return true;
        } catch {
          return false;
        }
      },
    },
  },
});
```

### overrideUserInfoOnSignIn [#overrideuserinfoonsignin]

一个布尔值，决定登录时是否覆盖数据库中的用户信息。默认是 `false`，即登录时不覆盖用户信息。如果希望每次登录都更新用户信息，设置为 `true`。

### requireEmailVerification [#requireemailverification]

要求在创建会话前验证此提供商的邮箱。默认为 `false`。

当提供商报告邮箱未验证时，Better Auth 仍会创建或关联用户和账户，但不会签发会话。OAuth 回调会重定向并附带 `?error=email_not_verified`，ID token 登录则返回带有 `EMAIL_NOT_VERIFIED` 错误代码的 `403`。系统会根据你的 `emailVerification` 设置发送（或重新发送）验证邮件：`sendOnSignUp` 负责新用户，`sendOnSignIn` 负责回访用户。配置 `emailVerification.sendVerificationEmail` 并保持启用 `sendOnSignUp`，以确保被阻止的用户始终能收到验证链接。

该限制会检查本地用户的验证状态，而不是每次请求中的提供商声明。已经通过其他方式（例如邮箱和密码）完成验证的用户，即使提供商之后报告邮箱未验证，也仍可继续访问。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      requireEmailVerification: true,
    },
  },
});
```

此选项需按提供商单独启用，并且独立于 `emailAndPassword.requireEmailVerification`：启用邮箱和密码验证不会限制社交登录。

<Callout type="warn">
  仅为报告可信 `email_verified` 信号的提供商启用此选项。有些提供商始终将邮箱报告为未验证（或从不返回邮箱），在这些提供商上启用该选项会阻止所有登录。请参阅[处理不提供邮箱的提供商](/docs/concepts/oauth#处理不提供邮箱的提供商)。
</Callout>

### mapProfileToUser [#mapprofiletouser]

使用 `mapProfileToUser` 更改默认的用户映射，或根据提供商个人资料填充其他用户字段。

Better Auth 会将该函数的返回值视为提供商输入，尽管该函数是在你的服务器上运行的。它会在 OAuth 注册、登录时覆盖个人资料，以及账户关联时同步个人资料的过程中，应用 `user.additionalFields` 中的输入规则。允许输入的映射字段会被解析并存储，而对于标记为 `input: false` 的字段，其映射值会被忽略。

资料映射无法重新定义提供商账户身份。内置提供商会根据其文档说明的不可变资料字段派生身份。当默认的 `sub` 或 `id` 字段不是正确的标识符时，Generic OAuth 提供商会使用 `accountSubject`。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      mapProfileToUser: (profile) => {
        return {
          firstName: profile.given_name,
          lastName: profile.family_name,
        };
      },
    },
  },
});
```

<Callout type="info">
  在 `user.additionalFields`
  [选项](/docs/concepts/database#扩展核心架构)中声明映射字段，
  并允许这些字段作为输入。无状态身份验证配置同样适用此规则。
</Callout>

#### 服务器拥有的字段与授权声明 [#服务器拥有的字段与授权声明]

将角色、封禁状态、内部标记和组织成员身份等安全敏感字段设置为 `input: false`。不要仅为了让 `mapProfileToUser` 持久化提供商声明就启用输入，因为同一设置也会允许通用注册和用户更新请求提供该字段。

`input` 和 `returned` 控制不同的方向。例如，`{ input: false, returned: true }` 定义了一个可读取的服务器拥有字段。API 输入和 `mapProfileToUser` 无法提供该字段，但 Better Auth 会在响应中包含其存储的值。

如果提供商声明决定了谁可以登录，请在 Better Auth 完成 OAuth 登录之前执行该策略。不要等到登录完成后再检查，因为 Better Auth 可能已经签发了有效会话。提供商存在专用选项时，优先使用该选项，例如 Google 提供商用于限制 Google Workspace 域名的 [`hd` 选项](/docs/authentication/google#将登录限制为-google-workspace)。对于调用 [`getUserInfo`](/docs/concepts/oauth#getuserinfo) 的流程，可以通过自定义实现验证提供商响应，并在策略验证失败时返回 `null`。对于不调用 `getUserInfo` 的独立登录路径，也要配置等效的强制执行逻辑。

如果还需要存储经过验证的声明，请将字段保持为 `input: false`，并使用应用程序的数据库层写入该字段。仅当静态值适用于通过该身份验证配置创建的每个用户时，才使用 `defaultValue`；不要将其用于存储从提供商个人资料中派生的值。

### refreshAccessToken [#refreshaccesstoken]

自定义刷新令牌的函数。此功能仅支持内置社交提供商（Google、Facebook、GitHub 等），暂不支持通过通用 OAuth 插件配置的自定义 OAuth 提供商。对于内置提供商，需按需提供刷新令牌的自定义函数。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      refreshAccessToken: async (token) => {
        return {
          accessToken: "new-access-token",
          refreshToken: "new-refresh-token",
        };
      },
    },
  },
});
```

### clientKey [#clientkey]

你的应用客户端密钥。TikTok 社交提供商使用此项替代 `clientId`。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    tiktok: {
      clientKey: "YOUR_TIKTOK_CLIENT_KEY",
      clientSecret: "YOUR_TIKTOK_CLIENT_SECRET",
    },
  },
});
```

### getUserInfo [#getuserinfo]

自定义函数，用于从提供商获取用户信息，覆盖默认的用户信息获取流程。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      getUserInfo: async (token) => {
        // 自定义实现获取用户信息
        const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
          headers: {
            Authorization: `Bearer ${token.accessToken}`,
          },
        });
        const profile = await response.json();
        return {
          user: {
            name: profile.name,
            email: profile.email,
            image: profile.picture,
            emailVerified: profile.verified_email,
          },
          data: profile,
        };
      },
    },
  },
});
```

### disableImplicitSignUp [#disableimplicitsignup]

禁用隐式注册新用户。启用后，登录时需要传入 `requestSignUp` 为 `true` 才能创建新用户。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      disableImplicitSignUp: true,
    },
  },
});
```

### prompt [#prompt]

授权码请求中使用的提示参数，控制认证流程行为。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      prompt: "select_account", // 或 "consent", "login", "none", "select_account+consent"
    },
  },
});
```

### responseMode [#responsemode]

授权码请求的响应模式，决定授权响应的返回方式。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      responseMode: "query", // 或 "form_post"
    },
  },
});
```

### disableDefaultScope [#disabledefaultscope]

移除提供商的默认权限范围。默认情况下，提供商会包含诸如 `email` 和 `profile` 的权限。设置为 `true` 后，会移除默认权限，只使用你指定的权限。

```ts title="auth.ts"
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  // 其他配置...
  socialProviders: {
    google: {
      clientId: "YOUR_GOOGLE_CLIENT_ID",
      clientSecret: "YOUR_GOOGLE_CLIENT_SECRET",
      disableDefaultScope: true,
      scope: ["https://www.googleapis.com/auth/userinfo.email"], // 只使用此权限
    },
  },
});
```

### 其他提供商配置 [#其他提供商配置]

每个提供商可能还有额外选项，请查看具体提供商文档了解更多细节。

