Skip to content
Auth

用 Apple 登录

Supabase Auth 支持在网页以及 iOS、macOS、watchOS 或 tvOS 的原生应用中使用 使用 Apple 登录

🌐 Supabase Auth supports using Sign in with Apple on the web and in native apps for iOS, macOS, watchOS or tvOS.

概览 #

🌐 Overview

要支持使用 Apple 登录,你需要在 Supabase 仪表板中为你的项目配置 Apple 提供者

🌐 To support Sign in with Apple, you need to configure the Apple provider in the Supabase dashboard for your project.

根据你想要构建的应用,有三种常见方法可以使用“使用 Apple 登录”:

🌐 There are three general ways to use Sign in with Apple, depending on the application you're trying to build:

在某些情况下,你可以在基于网页的原生应用中使用 OAuth 流程,比如使用 React NativeExpo 或其他类似框架。不过,最好在这些平台上使用原生的 Apple 登录功能。

🌐 In some cases you're able to use the OAuth flow within web-based native apps such as with React Native, Expo or other similar frameworks. It is best practice to use native Sign in with Apple capabilities on those platforms instead.

在使用 Expo 开发时,你可以通过 Expo Go 应用测试“使用 Apple 登录”,在其他情况下,你需要获取一个 Apple 开发者 账户来启用该功能。

🌐 When developing with Expo, you can test Sign in with Apple via the Expo Go app, in all other cases you will need to obtain an Apple Developer account to enable the capability.

在网页上使用 OAuth 流程#

使用苹果的 OAuth 流程登录是为网页或基于浏览器的登录方式设计的。它可以用于基于网页的应用以及网站,不过有些用户可以通过直接使用 Apple JS 登录获得好处。

在背后,Supabase Auth 使用 Apple 提供的 REST APIs

要开始登录,你可以使用 Supabase JavaScript 库里的 signInWithOAuth() 方法:

1
import { createClient } from '@supabase/supabase-js'
2
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
3
4
// ---cut---
5
supabase.auth.signInWithOAuth({
6
provider: 'apple',
7
})

这个调用会带用户进入苹果的同意屏幕。流程结束后,用户的个人信息会与 Supabase Auth 进行交换和验证,然后再重定向回你的网页应用,同时带上表示用户会话的访问令牌和刷新令牌。

以 PKCE 流程为例,比如在服务端认证中,你需要一个额外的步骤来处理代码交换。在调用 signInWithOAuth 时,提供一个指向回调路由的 redirectTo URL。这个重定向 URL 应该添加到你的 重定向允许列表 中。

🌐 For a PKCE flow, for example in Server-Side Auth, you need an extra step to handle the code exchange. When calling signInWithOAuth, provide a redirectTo URL which points to a callback route. This redirect URL should be added to your redirect allow list.

在浏览器中,signInWithOAuth 会自动重定向到 OAuth 提供商的认证端点,然后再重定向到你的端点。

🌐 In the browser, signInWithOAuth automatically redirects to the OAuth provider's authentication endpoint, which then redirects to your endpoint.

1
import { createClient, type Provider } from '@supabase/supabase-js';
2
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
3
const provider = 'provider' as Provider
4
5
// ---cut---
6
await supabase.auth.signInWithOAuth({
7
provider,
8
options: {
9
redirectTo: `http://example.com/auth/callback`,
10
},
11
})

在回调端点,处理代码交换以保存用户会话。

🌐 At the callback endpoint, handle the code exchange to save the user session.

app/auth/callback/route.ts 创建一个新文件,并填入以下内容:

🌐 Create a new file at app/auth/callback/route.ts and populate with the following:

app/auth/callback/route.ts
1
import { NextResponse } from 'next/server'
2
3
// The client you created from the Server-Side Auth instructions
4
import { createClient } from '@/utils/supabase/server'
5
6
export async function GET(request: Request) {
7
const { searchParams, origin } = new URL(request.url)
8
const code = searchParams.get('code')
9
// if "next" is in param, use it as the redirect URL
10
let next = searchParams.get('next') ?? '/'
11
if (!next.startsWith('/')) {
12
// if "next" is not a relative URL, use the default
13
next = '/'
14
}
15
16
if (code) {
17
const supabase = await createClient()
18
const { error } = await supabase.auth.exchangeCodeForSession(code)
19
if (!error) {
20
const forwardedHost = request.headers.get('x-forwarded-host') // original origin before load balancer
21
const isLocalEnv = process.env.NODE_ENV === 'development'
22
if (isLocalEnv) {
23
// we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host
24
return NextResponse.redirect(`${origin}${next}`)
25
} else if (forwardedHost) {
26
return NextResponse.redirect(`https://${forwardedHost}${next}`)
27
} else {
28
return NextResponse.redirect(`${origin}${next}`)
29
}
30
}
31
}
32
33
// return the user to an error page with instructions
34
return NextResponse.redirect(`${origin}/auth/auth-code-error`)
35
}

配置 #

你需要以下信息:

  1. 你的 Apple 开发者账号的 团队 ID,这是一个由 10 个字符组成的字母数字串,用于唯一标识应用的开发者。通常可以在 Apple 开发者控制台右上角的菜单中找到。
  2. 在 Apple 开发者控制台的 服务 部分为“使用 Apple 登录进行邮件通信”注册邮箱来源。这可以让 Apple 在用户选择隐藏邮箱地址时,通过你的域发送中转邮件。
  3. 一个应用 ID,用于唯一标识你正在构建的应用。你可以在 Apple 开发者控制台的 Identifiers 部分创建一个新的应用 ID(使用右上角的筛选菜单可以查看所有应用 ID)。这些通常是反向域名字符串,例如 com.example.app。创建应用 ID 后,别忘了在功能列表中配置“使用 Apple 登录”。目前 Supabase Auth 不支持服务器到服务器的通知端点,所以这项设置可以留空。(过去,应用 ID 被称为 bundle ID。)
  4. 一个 服务 ID,用来唯一标识你在上一步注册的应用提供的网络服务。你可以在 Apple 开发者后台的 标识符 页面创建新的服务 ID(使用右上角的筛选菜单可以查看所有服务 ID)。它们通常是一个反向域名字符串,例如 com.example.app.web
  5. 为新创建的 Services ID 配置网站 URL。你应该使用的网页域名是你的 Supabase 项目所在的域名。通常这是 <project-id>.supabase.co,而重定向 URL 是 https://<project-id>.supabase.co/auth/v1/callback
  6. 在 Apple 开发者控制台的 Keys 部分创建一个签名 Key。你可以使用这个密钥通过下面的工具生成一个密钥,然后将其添加到你的 Supabase 项目的 Auth 配置中。务必妥善保存 AuthKey_XXXXXXXXXX.p8 文件。如果你不小心丢失或意外公开了它,请立即在 Apple 开发者控制台撤销并创建一个新的。
  7. 最后,将你在上面配置的信息添加到 Supabase 控制台中的 Apple 提供商配置 中。如果你的项目也使用原生的“使用 Apple 登录”(例如在 iOS、Expo 或 Flutter 上),请将这个服务 ID 列为 Client IDs 字段的第一个条目。Supabase 会在列表中使用第一个客户端 ID 来处理网页 signInWithOAuth 流程,而原生的 signInWithIdToken 流程则接受列表中的任意客户端 ID 作为有效的令牌受众,不管顺序如何。如果原生 App ID 放在服务 ID 前面,原生登录仍然可以使用,但网页登录会被 Apple 拒绝。

你也可以使用管理 API 配置苹果认证提供商:

🌐 You can also configure the Apple auth provider using the Management API:

1
# Get your access token from https://supabase.com/dashboard/account/tokens
2
export SUPABASE_ACCESS_TOKEN="your-access-token"
3
export PROJECT_REF="your-project-ref"
4
5
# Configure Apple auth provider
6
curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
7
-H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
8
-H "Content-Type: application/json" \
9
-d '{
10
"external_apple_enabled": true,
11
"external_apple_client_id": "your-services-id",
12
"external_apple_secret": "your-generated-secret-key"
13
}'

使用 Apple JS 登录#

使用 Apple JS 登录 是苹果官方提供的用于在网站上认证 Apple 用户的框架。虽然它可以用于基于网页的应用,但对于那些场景,上面描述的 OAuth 流程会更有优势。我们建议只在传统网站上使用这种方法。

一旦用户通过 Apple JS 登录同意,你可以在网站上使用 Supabase JavaScript 库的 signInWithIdToken() 方法来获取访问令牌和刷新令牌:

1
async function signIn() {
2
try {
3
// Generate a nonce for security
4
const nonce = crypto.randomUUID() // or use your preferred nonce generation method
5
6
const data = await AppleID.auth.signIn()
7
8
const { data: authData, error } = await supabase.auth.signInWithIdToken({
9
provider: 'apple',
10
token: data.id_token,
11
nonce: nonce,
12
})
13
14
if (error) {
15
throw error
16
}
17
18
// Apple only provides the user's name on the first sign-in
19
// The user object contains name information from Apple's response
20
if (data.user && data.user.name) {
21
const fullName = [
22
data.user.name.firstName,
23
data.user.name.middleName,
24
data.user.name.lastName
25
].filter(Boolean).join(' ')
26
27
// Save the name to user metadata for future use
28
await supabase.auth.updateUser({
29
data: {
30
full_name: fullName,
31
given_name: data.user.name.firstName,
32
family_name: data.user.name.lastName,
33
}
34
})
35
}
36
} catch (error) {
37
console.error('Apple sign in failed:', error)
38
// Handle sign-in errors appropriately
39
}
40
}

或者,你可以使用带有 usePopup 选项的 AppleIDSignInOnSuccess 事件:

1
// Generate and store nonce for verification
2
const nonce = crypto.randomUUID()
3
4
// Initialize Apple ID with nonce
5
AppleID.auth.init({
6
clientId: 'your-services-id',
7
scope: 'name email',
8
redirectURI: 'https://your-domain.com/auth/callback',
9
usePopup: true,
10
nonce: nonce,
11
})
12
13
// Listen for authorization success
14
document.addEventListener('AppleIDSignInOnSuccess', async (event) => {
15
try {
16
const { data: authData, error } = await supabase.auth.signInWithIdToken({
17
provider: 'apple',
18
token: event.detail.authorization.id_token,
19
nonce: nonce,
20
})
21
22
if (error) {
23
throw error
24
}
25
26
// Apple only provides the user's name on the first sign-in
27
if (event.detail.user && event.detail.user.name) {
28
const fullName = [
29
event.detail.user.name.firstName,
30
event.detail.user.name.middleName,
31
event.detail.user.name.lastName
32
].filter(Boolean).join(' ')
33
34
// Save the name to user metadata for future use
35
await supabase.auth.updateUser({
36
data: {
37
full_name: fullName,
38
given_name: event.detail.user.name.firstName,
39
family_name: event.detail.user.name.lastName,
40
}
41
})
42
}
43
} catch (error) {
44
console.error('Apple sign in failed:', error)
45
}
46
})

初始化库时,确保像上面的例子一样请求范围 name email

配置 #

要使用 Apple JS 登录,你需要配置这些选项:

  1. 拥有一个 App ID 来唯一标识你正在构建的应用。你可以在 Apple 开发者控制台的 Identifiers 部分创建一个新的 App ID(使用右上角的筛选菜单可以查看所有 App ID)。这些通常是倒置的域名字符串,例如 com.example.app。确保你在创建的或已有的 App ID 的功能列表中配置了“使用 Apple 登录”。目前 Supabase Auth 不支持服务器到服务器的通知端点,所以你可以把该设置留空。(以前 App ID 被称为 bundle ID。)
  2. 获取一个附加到 App ID 的 服务 ID,它可以唯一标识网站。在初始化使用 Apple JS 的登录时,把这个值作为客户端 ID。你可以在 Apple 开发者控制台的 Identifiers 部分创建一个新的服务 ID(使用右上角的筛选菜单可以看到所有服务 ID)。这些通常是一个反向域名字符串,例如 com.example.app.website
  3. 为新创建的 Services ID 配置网站 URL。你应该使用托管你网站的域名。重定向 URL 也必须指向你网站上的一个页面,用于接收来自 Apple 的回调。
  4. 将你创建的服务 ID 注册到你项目在 Supabase 仪表板中的 Apple 提供者配置 下的 客户端 ID