Apple
Apple 提供者的设置与使用。
获取 OAuth 凭证
使用 Apple 登录时,您需要一个客户端 ID、团队 ID、密钥 ID 和私钥。您可以从 Apple 开发者门户 获取它们,然后用它们生成客户端密钥 JWT。
您需要一个有效的 Apple 开发者账户 才能访问开发者门户并生成这些凭证。
按照以下步骤设置您的 App ID、服务 ID 并生成客户端密钥所需的密钥:
-
导航到证书、标识符与配置文件: 在 Apple 开发者门户中,转到“证书、标识符与配置文件”部分。
-
创建 App ID:
- 进入“标识符”标签页。
- 点击标识符旁边的
+图标。 - 选择
App IDs,然后点击继续。 - 选择
App作为类型,然后点击继续。 - 描述: 输入您的应用名称(例如,“我的出色应用”)。此名称在用户登录时可能会显示给用户。
- 捆绑标识符: 设置一个捆绑标识符。推荐的格式是反向域名(例如,
com.yourcompany.yourapp)。使用.ai(用于应用标识符)等后缀有助于组织,但不是必需的(例如,com.yourcompany.yourapp.ai)。 - 向下滚动到 功能。选择
使用 Apple 登录的复选框。 - 点击
继续,然后点击注册。
-
创建服务 ID:
- 返回“标识符”标签页。
- 点击
+图标。 - 选择
服务 ID,然后点击继续。 - 描述: 输入此服务的描述(例如,再次输入您的应用名称)。
- 标识符: 为服务设置一个唯一标识符。将其设置为与您的 App ID 不同的反向域名格式(例如,
com.yourcompany.yourapp.si,其中.si表示服务标识符——这仅用于您的组织,并非必需)。此服务 ID 将作为您的clientId。 - 点击
继续,然后点击注册。
-
配置服务 ID:
- 在
标识符列表中找到您刚刚创建的服务 ID 并点击它。 - 勾选
使用 Apple 登录的功能,然后点击配置。 - 在 主 App ID 下,选择您之前创建的 App ID(例如,
com.yourcompany.yourapp.ai)。 - 在 域和子域 下,列出您将用于 Apple 登录的所有根域(例如,
example.com、anotherdomain.com)。 - 在 返回 URL 中,输入回调 URL:
https://yourdomain.com/api/auth/callback/apple。添加所有必要的返回 URL。 - 点击
下一步,然后点击完成。 - 点击
继续,然后点击保存。
- 在
-
创建客户端密钥:
- 进入
密钥标签页。 - 点击
+图标以创建新密钥。 - 密钥名称: 输入密钥名称(例如,“使用 Apple 登录密钥”)。
- 向下滚动并选择
使用 Apple 登录的复选框。 - 点击
使用 Apple 登录旁边的配置按钮。 - 选择您之前创建的 主 App ID。
- 点击
保存,然后点击继续,再点击注册。 - 下载密钥: 立即下载
.p8密钥文件。此文件只能下载一次。 记下密钥 ID(在创建密钥后可在密钥页面找到)和您的团队 ID(可在 Apple 开发者账户设置中找到)。
- 进入
-
生成客户端密钥(JWT): Apple 要求使用下载的
.p8密钥、密钥 ID 和团队 ID 动态生成 JSON Web 令牌(JWT)。此 JWT 作为您的clientSecret。我们建议您在身份验证配置中使用下方的
jose示例 生成客户端密钥 JWT。您也可以按照 Apple 的文档 手动生成,然后将生成的 JWT 作为clientSecret传入。注意: Apple 会拒绝未来过期时间超过 15,777,000 秒(六个月)的客户端密钥 JWT。为了保持身份验证不中断,您需要在过期前重新生成客户端密钥。
Apple 的 OAuth 实现要求 clientSecret 使用 JWT(JSON Web 令牌),而不是共享密钥字符串。
根据 Apple 文档,这些 JWT:
- 未来过期时间不能超过 15,777,000 秒(六个月)
- 必须使用您的私钥进行加密签名
安装 jose 包以生成客户端密钥 JWT。
npm install jose配置提供者
要配置提供者,您需要将其添加到身份验证实例的 socialProviders 选项中。
您还需要在身份验证实例配置中将 https://appleid.apple.com 添加到 trustedOrigins 数组中,以允许与 Apple 的身份验证服务器通信。
import { betterAuth } from "better-auth"
import { importPKCS8, SignJWT } from "jose";
// 生成 '使用 Apple 登录' 所需的客户端密钥 JWT。
async function generateAppleClientSecret(clientId, teamId, keyId, privateKey) {
const key = await importPKCS8(privateKey, "ES256");
const now = Math.floor(Date.now() / 1000);
return new SignJWT({})
.setProtectedHeader({ alg: "ES256", kid: keyId })
.setIssuer(teamId)
.setSubject(clientId)
.setAudience("https://appleid.apple.com")
.setIssuedAt(now)
.setExpirationTime(now + 180 * 24 * 60 * 60)
.sign(key);
}
export const auth = betterAuth({
socialProviders: {
apple: async () => ({
clientId: process.env.APPLE_CLIENT_ID as string,
clientSecret: await generateAppleClientSecret(
process.env.APPLE_CLIENT_ID!,
process.env.APPLE_TEAM_ID!,
process.env.APPLE_KEY_ID!,
process.env.APPLE_PRIVATE_KEY!,
),
// 可选
appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER as string,
}),
},
// 为 Apple 登录流程添加 appleid.apple.com 到受信任来源
trustedOrigins: ["https://appleid.apple.com"],
})在原生 iOS 上,它使用的不是服务 ID,而是应用 ID(bundle ID)作为客户端 ID,因此如果在 signIn.social 中将服务 ID 作为 clientId 并使用 idToken,则会抛出错误:JWTClaimValidationFailed: unexpected "aud" claim value。因此,当您想使用 ID Token 通过 Apple 登录时,需要提供 appBundleIdentifier。
对于多个静态受众(例如服务 ID 加原生 bundle ID),请优先使用 clientId: string[] 或 audience: string[],而不是仅设置 appBundleIdentifier。当受众必须依赖请求时(例如读取 x-platform 头),请使用自定义的 verifyIdToken 回调——它会将请求上下文作为第三个参数传入。
Apple 仅在首次授权时返回 email 声明;此后的每次登录都会省略它,而且 Apple 不提供可稍后获取它的用户信息端点。有关持久化的指导以及 mapProfileToUser 回退,请参见 Handling Providers Without Email。
本地主机和非 TLS 限制
Apple 登录不支持 localhost 或非 HTTPS URL。开发期间:
- 您不能使用
http://localhost作为返回 URL - 您必须使用具有有效 HTTPS/TLS 证书的域
此限制由 Apple 的安全要求强制执行,无法绕过。
使用方法
Apple 登录
要使用 Apple 登录,您可以使用客户端提供的 signIn.social 函数。signIn 函数接收一个包含以下属性的对象:
provider:要使用的提供者。应设置为apple。
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "apple"
})
}使用 ID 令牌进行 Apple 登录
要使用 ID 令牌进行 Apple 登录,您可以使用 signIn.social 函数传递 ID 令牌。
当您在客户端拥有 Apple 的 ID 令牌并希望在服务器端使用它进行登录时,这非常有用。
如果提供了 ID 令牌,则不会发生重定向,用户将直接登录。
await authClient.signIn.social({
provider: "apple",
idToken: {
token: // Apple ID 令牌,
nonce: // 随机数(可选)
accessToken: // 访问令牌(可选)
}
})生成 Apple 客户端密钥(JWT)
如果您之前使用过本节中的页面内生成器,请改用上面的 jose 示例。它使用 Better Auth 的异步提供者配置,通过您的客户端 ID、团队 ID、密钥 ID 和私钥生成 Apple 客户端密钥 JWT。
该示例使用 180 天的过期时间,低于 Apple 的六个月限制。通过配置生成令牌,可以避免将静态生成的令牌复制到您的身份验证设置中。
或者,您仍然可以按照 Apple 的文档 手动生成客户端密钥 JWT,然后将生成的 JWT 作为 clientSecret 传入。如果您选择这种方式,请记得在它过期前替换它。