Sign in with Apple 接入分四步:App ID 开启能力并重新生成描述文件,entitlements 加 com.apple.developer.applesignin,用 SignInWithAppleButton 拿到 identityToken 交服务端验证,删除账号时调用 revoke。
Sign in with Apple(通过 Apple 登录)接入 iOS App 分四步:给 App ID 开启 Sign in with Apple 能力并重新生成描述文件,在 entitlements 里加上 com.apple.developer.applesignin,用 SwiftUI 的 SignInWithAppleButton 拿到凭证并把 identityToken 交给服务端验证,最后处理授权撤销和账号删除时的 revoke。
下文从开发者后台配置一直讲到在自己的 iPhone 上登录成功,Apple 规则截至 2026 年 10 月,都附了官方链接。开始前准备好这几样东西。
- 已加入 Apple Developer Program 的账号,角色是账户持有人或管理员,开启能力需要这两种角色之一。
- 一个能用 KXBuild 装到 iPhone 上运行的 SwiftUI 工程,部署目标 iOS 15 或更高。还没有的按 [SwiftUI 从新建工程到装上 iPhone]做一个。
- 一部登录了 Apple 账户、开启了双重认证的 iPhone,并打开了开发者模式,开关位置见[开发者模式怎么打开]。
- 一个你自己的后端接口,用来验证登录凭证、建立账号。文中用
https://api.example.com/auth/apple代替。
什么时候必须提供「通过 Apple 登录」?
App Store 审核指南 [4.8 登录服务]规定,App 用第三方或社交登录(例如 Facebook、Google、微信登录)来创建或验证用户的主账号时,必须同时提供另一种等效的登录方式。这种登录方式要满足三条:只收集用户的姓名和邮箱;允许用户在建账号时隐藏邮箱;未经同意不收集用户在 App 里的行为用于广告。「通过 Apple 登录」满足这三条,是最常见的选择。
下面这些情况不需要另外提供。
| 情况 | 例子 |
|---|---|
| 只用你公司自己的账号体系 | 手机号加验证码、自家用户名密码 |
| 教育、企业或商务 App,要求用已有的学校或企业账号登录 | 企业内部办公 App |
| 用政府或行业背书的公民身份或电子身份认证 | 电子身份证登录 |
| App 是某个第三方服务的客户端,用户要直接登录那个服务才能看到内容 | 邮件客户端、某个社交平台的专用客户端 |
| 替代应用市场,或从替代应用市场分发、使用该市场的专用登录 | — |
另外两条和登录相关的规定也要一起满足。审核指南 [5.1.1(v) 要求支持创建账号的 App 必须在 App 内提供删除账号的入口。
Apple 的[账号删除说明]写明,使用「通过 Apple 登录」的 App 在用户删除账号时,要调用 Sign in with Apple REST API 撤销用户的令牌,做法见下文 [revoke 一节]。审核由 Apple 决定,常见的被拒原因见 [App 审核被拒的常见原因]。
接入 Sign in with Apple 要做哪几步?
| 步骤 | 在哪里做 | 产出 |
|---|---|---|
| 1. App ID 开启 Sign in with Apple | developer.apple.com 的 Identifiers | App ID 带上这项能力 |
| 2. 重新生成描述文件 | developer.apple.com 的 Profiles | 含 com.apple.developer.applesignin 的描述文件 |
| 3. 加 entitlements | VS Code | MyApp.entitlements |
| 4. 写登录代码 | VS Code | 登录按钮、凭证处理、状态检查 |
| 5. 创建 Sign in with Apple 私钥 | developer.apple.com 的 Keys | 服务端用来生成 client_secret 的 .p8 |
| 6. 服务端验证和 revoke | 你的后端 | 验证 identityToken、换取和撤销令牌 |
| 7. 真机测试 | iPhone | 第一次登录、再次登录、撤销后重新登录 |
第 1 步:App ID 怎么开启 Sign in with Apple?
- 登录 developer.apple.com,进入 Certificates, Identifiers & Profiles,在 Identifiers 页点开 App 的 App ID。App ID 要是精确的 Bundle ID,例如
com.example.myapp。 - 在 Capabilities 列表勾选 Sign in with Apple,点旁边的 Edit 或 Configure。
- 选 Enable as a primary App ID(作为主 App ID 启用),保存。第一次启用、或者这是一个新 App 时都选这一项。
- 点 Save 保存 App ID。
同一个产品还有 Mac 版、网页版要共用账号的,那些 App ID 选「与已有的主 App ID 分组」,用户只需授权一次。规则见 Apple 的[关于通过 Apple 登录]。
第 2 步:为什么要重新生成描述文件?
描述文件里记录着 App ID 当时开通的能力,开启 Sign in with Apple 之前生成的描述文件不含这项能力,必须重新生成。
- 在 Profiles 页逐个点开这个 App ID 的描述文件(Development、Ad Hoc、App Store),点 Edit,直接 Save,再 Download。
- 用新文件替换工程
keychain目录里的旧文件,旧的删掉。目录里同时留着新旧两份时,构建可能匹配到不含这项能力的旧文件。
在 VS Code 里双击新的 .mobileprovision 用描述文件查看器打开,权限里出现 com.apple.developer.applesignin 就说明已经包含这项能力。证书目录的放法和挑选规则见[证书与签名],App ID 和描述文件的关系见 [Bundle ID、App ID、证书和描述文件的关系]。
第 3 步:给工程加 entitlements
在 App 源码文件夹里新建 MyApp.entitlements,内容如下。工程原本已有 entitlements 文件的(例如开了推送或 App Groups),把这个键加进原文件,不用新建。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
</dict>
</plist>
键名和取值以 Apple 的 [applesignin entitlement 文档]为准,值是只含一项 Default 的数组。
然后让 target 用这个文件。以下两种做法任选其一。
- VS Code 插件。右键 target 选「KXBuild: 编辑目标设置」,在 General 页 Signing 分组的「Entitlements 文件」里填
MyApp/MyApp.entitlements,Ctrl+S 保存。 - 命令行。在工程目录执行下面这条,路径相对于 .xcodeproj 所在目录。
kxbuild project add-setting -k CODE_SIGN_ENTITLEMENTS -v MyApp/MyApp.entitlements -t MyApp -P .\MyApp.xcodeproj
以后要加其他能力,双击 .entitlements 用[属性列表编辑器]编辑即可。设置项的位置见[工程设置编辑器]。
第 4 步:用 Swift 写登录代码
下面三个文件适合部署目标 iOS 15 及以上,在 Swift 6 严格并发模式下也能编译。SignInWithAppleButton 来自 SwiftUI,凭证类型来自 AuthenticationServices 框架。
用钥匙串保存用户标识
用户标识要存在钥匙串里,不要放 UserDefaults。新建 KeychainStore.swift。
import Foundation
import Security
/// 把少量字符串存进钥匙串,删 App 重装后通常还在
enum KeychainStore {
private static let service = "com.example.myapp.auth"
static func save(_ value: String, for key: String) {
delete(key)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecValueData as String: Data(value.utf8),
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
]
SecItemAdd(query as CFDictionary, nil)
}
static func read(_ key: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
]
var item: CFTypeRef?
guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess,
let data = item as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
static func delete(_ key: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
]
SecItemDelete(query as CFDictionary)
}
}
登录管理器
新建 AppleSignInManager.swift,负责准备请求、处理凭证、把令牌发给服务端、检查凭证状态。把 loginURL 换成你自己的接口。
import Foundation
import AuthenticationServices
import CryptoKit
/// 发给自己服务器的登录请求体
struct AppleLoginRequest: Codable {
let identityToken: String
let authorizationCode: String
let rawNonce: String
let user: String
let email: String?
let givenName: String?
let familyName: String?
}
@MainActor
final class AppleSignInManager: ObservableObject {
// 换成你自己服务器的登录接口
static let loginURL = URL(string: "https://api.example.com/auth/apple")!
@Published private(set) var userID: String? = KeychainStore.read("appleUserID")
@Published var message: String?
private var currentNonce: String?
private var revokeObserver: NSObjectProtocol?
var isSignedIn: Bool { userID != nil }
init() {
// 用户在系统设置里停用了「通过 Apple 登录」时,系统发这个通知
revokeObserver = NotificationCenter.default.addObserver(
forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification,
object: nil,
queue: .main
) { [weak self] _ in
Task { @MainActor in
self?.signOut(message: "Apple 账户授权已撤销,请重新登录")
}
}
}
/// 点按钮时调用:要姓名和邮箱,并带上 nonce
func prepare(_ request: ASAuthorizationAppleIDRequest) {
let nonce = Self.randomNonce()
currentNonce = nonce
request.requestedScopes = [.fullName, .email]
request.nonce = Self.sha256(nonce)
}
/// 处理按钮的回调结果
func handle(_ result: Result<ASAuthorization, Error>) async {
switch result {
case .success(let authorization):
guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else {
message = "收到的不是 Apple ID 凭证"
return
}
await signIn(with: credential)
case .failure(let error):
if let authError = error as? ASAuthorizationError, authError.code == .canceled {
return // 用户自己点了取消,不提示
}
message = "登录失败:\(error.localizedDescription)"
}
}
private func signIn(with credential: ASAuthorizationAppleIDCredential) async {
// user 是这个用户在你的开发者团队下固定不变的标识,用它当账号主键
let user = credential.user
// 邮箱和姓名只在第一次授权时返回,拿到就先存下来
if let email = credential.email {
KeychainStore.save(email, for: "appleEmail")
}
if let name = credential.fullName {
let formatted = PersonNameComponentsFormatter().string(from: name)
if !formatted.isEmpty {
KeychainStore.save(formatted, for: "appleFullName")
}
}
guard let tokenData = credential.identityToken,
let identityToken = String(data: tokenData, encoding: .utf8),
let codeData = credential.authorizationCode,
let authorizationCode = String(data: codeData, encoding: .utf8),
let rawNonce = currentNonce else {
message = "没有拿到 identityToken,请重试"
return
}
let body = AppleLoginRequest(
identityToken: identityToken,
authorizationCode: authorizationCode,
rawNonce: rawNonce,
user: user,
email: credential.email ?? KeychainStore.read("appleEmail"),
givenName: credential.fullName?.givenName,
familyName: credential.fullName?.familyName
)
do {
var request = URLRequest(url: Self.loginURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(body)
let (_, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
message = "服务器验证失败"
return
}
KeychainStore.save(user, for: "appleUserID")
userID = user
message = nil
} catch {
message = "网络错误:\(error.localizedDescription)"
}
}
/// 启动时和回到前台时调用,确认授权还有效
func checkCredentialState() async {
guard let userID else { return }
do {
let state = try await ASAuthorizationAppleIDProvider().credentialState(forUserID: userID)
switch state {
case .authorized:
break
case .revoked:
signOut(message: "Apple 账户授权已撤销,请重新登录")
case .notFound:
signOut(message: "请重新登录")
case .transferred:
message = "App 已转移到新的开发者团队,需要迁移账号"
@unknown default:
break
}
} catch {
// 查询失败(例如没登录 Apple 账户)时保持现状,下次再查
print("getCredentialState 失败: \(error)")
}
}
func signOut(message: String? = nil) {
KeychainStore.delete("appleUserID")
userID = nil
self.message = message
}
// MARK: - nonce
static func randomNonce(length: Int = 32) -> String {
let charset = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._")
var generator = SystemRandomNumberGenerator()
return String((0..<length).map { _ in charset.randomElement(using: &generator)! })
}
static func sha256(_ input: String) -> String {
SHA256.hash(data: Data(input.utf8)).map { String(format: "%02x", $0) }.joined()
}
}
几个要点。
user当账号主键。Apple 的文档写明,用户标识在你的开发者团队下唯一且不变,要用它而不是邮箱来识别用户。用户隐藏了邮箱时,你拿到的是一个privaterelay.appleid.com的转发地址。- 姓名和邮箱只给一次。按 Apple 的[用户认证说明],姓名和邮箱只在用户第一次用「通过 Apple 登录」授权你的 App 时返回,之后在别的设备上登录也不会再给。用户停用后重新授权才会再次返回。
- 拿到就保存。拿到后要立刻保存,并尽快传给服务端,网络失败时下次还能补发。之后的
identityToken里仍带邮箱,但不带姓名。 - nonce 防重放。请求里放的是随机串的 SHA-256,
identityToken里的nonce字段就是这个哈希值。原始随机串发给服务端,服务端算一次 SHA-256 再比对。 .canceled不当错误。用户在登录框点了取消会走到这里,不用弹提示。
登录界面和 App 入口
新建 LoginView.swift,Swift 工程的基本配置见 [Windows 开发 Swift iOS App]。按钮的样式跟随深色、浅色模式切换,深色背景上用白色按钮。
import SwiftUI
import AuthenticationServices
struct LoginView: View {
@EnvironmentObject private var auth: AppleSignInManager
@Environment(\.colorScheme) private var colorScheme
var body: some View {
VStack(spacing: 20) {
if auth.isSignedIn {
Text("已登录")
.font(.title2.bold())
Text(KeychainStore.read("appleFullName") ?? "Apple 用户")
Text(KeychainStore.read("appleEmail") ?? "未提供邮箱")
.foregroundStyle(.secondary)
Button("退出登录") {
auth.signOut()
}
} else {
SignInWithAppleButton(.signIn) { request in
auth.prepare(request)
} onCompletion: { result in
Task { await auth.handle(result) }
}
.signInWithAppleButtonStyle(colorScheme == .dark ? .white : .black)
.frame(height: 50)
}
if let message = auth.message {
Text(message)
.font(.footnote)
.foregroundStyle(.red)
}
}
.padding(24)
}
}
把 App 入口文件(例如 MyAppApp.swift)改成下面这样。启动时和每次回到前台时都检查一次凭证状态。
import SwiftUI
@main
struct MyAppApp: App {
@StateObject private var auth = AppleSignInManager()
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
LoginView()
.environmentObject(auth)
.task { await auth.checkCredentialState() }
}
.onChange(of: scenePhase) { phase in
if phase == .active {
Task { await auth.checkCredentialState() }
}
}
}
}
部署目标是 iOS 17 及以上的,onChange 可以改用两个参数的新写法 { _, phase in ... },旧写法会有弃用警告但不影响运行。UIKit 工程用 ASAuthorizationAppleIDButton 加 ASAuthorizationController,凭证处理部分和上面相同,写法见 Apple 的[示例项目]。
凭证里有什么
| 字段 | 内容 | 什么时候有 |
|---|---|---|
user | 用户标识,团队内唯一且不变 | 每次都有 |
email | 真实邮箱或转发邮箱 | 只在第一次授权时有 |
fullName | 姓名,用户可以在授权框里改 | 只在第一次授权时有 |
identityToken | JWT,交给服务端验证 | 每次都有 |
authorizationCode | 一次性授权码,服务端拿它换 refresh token | 每次都有 |
realUserStatus | 是否像真人,likelyReal、unknown、unsupported | 只在第一次授权时返回 |
用户停用授权后 App 怎么知道?
用户可以随时在手机设置里停用你的 App 的「通过 Apple 登录」,所以 App 要主动确认授权还在。getCredentialState(forUserID:completion:) 在 Swift 里可以写成 try await credentialState(forUserID:),这是一个本地调用,不走网络,可以放心在每次回到前台时调用。Apple 的[验证用户]一文建议把 App 的登录会话和这个调用的结果绑在一起。
| 状态 | 含义 | 处理 |
|---|---|---|
.authorized | 授权有效 | 继续使用 |
.revoked | 用户已撤销授权 | 退出登录,回到登录页 |
.notFound | 这个用户和你的 App 没有授权关系 | 退出登录,回到登录页 |
.transferred | App 已转移到其他开发者团队 | 按 Apple 的团队转移流程迁移用户标识 |
App 在运行时,用户撤销授权,系统会发出 credentialRevokedNotification,代码里已经监听它并退出登录。用户删除 Apple 账户时系统不发这个通知,Apple 文档要求用 getCredentialState 来发现,所以前台检查不能省。
服务端怎么验证 identityToken?
App 拿到的凭证必须在服务端验证后才能建立登录会话。服务端要做三件事:验证 identityToken、用 authorizationCode 换 refresh token、在用户删除账号时撤销令牌。
验证 identityToken 的五项检查
| 检查 | 怎么做 |
|---|---|
| 签名 | 从 https://appleid.apple.com/auth/keys 取 Apple 的公钥集合,用 JWT 头里 kid 对应的那把公钥、按头里的 alg 验签 |
iss | 必须是 https://appleid.apple.com |
aud | 必须是你的 client_id,iOS App 就是它的 Bundle ID |
exp | 当前时间要早于过期时间 |
nonce | 等于 App 发来的原始随机串的 SHA-256 |
通过后再核对 sub 字段等于 App 发来的 user,以 sub 作为账号主键。检查项出自 Apple 的[验证用户]。公钥集合里可能有多把,数量也会变,不要写死,按 kid 挑选并缓存一段时间。
创建 Sign in with Apple 私钥
服务端调用 Apple 的令牌接口要带 client_secret,它是一个用 Sign in with Apple 私钥签名的 JWT。
- 在 Certificates, Identifiers & Profiles 的 Keys 页点「+」,填名字,勾选 Sign in with Apple,点旁边的 Configure。
- 选择关联的主 App ID,保存后点 Continue、Register。
- 下载
.p8文件,记下 Key ID。这个文件同样只能下载一次。
每个主 App ID 最多关联两把私钥,见 Apple 的[创建 Sign in with Apple 私钥]。这把私钥和推送用的 APNs 密钥是两回事,推送密钥的区别见 [推送用 .p8 还是 .p12]。
client_secret 的 JWT 内容如下,用 ES256 签名。
{
"alg": "ES256",
"kid": "你的 Key ID"
}
{
"iss": "你的 Team ID",
"iat": 1792000000,
"exp": 1807000000,
"aud": "https://appleid.apple.com",
"sub": "com.example.myapp"
}
sub 填 client_id,也就是 Bundle ID,区分大小写。exp 最多比当前时间晚 15777000 秒(六个月),超过会被拒,规则见 Apple 的[创建 client secret]。可以每几个月在服务端重新生成一次。
用授权码换 refresh token
curl -X POST "https://appleid.apple.com/auth/token" \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=com.example.myapp' \
-d 'client_secret=上一步生成的 JWT' \
-d 'code=App 发来的 authorizationCode' \
-d 'grant_type=authorization_code'
成功时返回 access_token、refresh_token 和 id_token。把 refresh_token 和账号一起存在服务端,删除账号时要用它。授权码只能用一次,App 每次登录拿到的都是新的。接口说明见 Apple 的[令牌验证]。
Apple 允许你最多每天用 refresh token 验证一次用户的 Apple 账户是否仍然正常,调用太频繁会被限流。
用户删除账号时怎么调用 revoke?
用户在 App 里删除账号时,服务端删除数据后再调用撤销接口,让这个用户对你的 App 的授权失效。
curl -X POST "https://appleid.apple.com/auth/revoke" \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=com.example.myapp' \
-d 'client_secret=同样的 client_secret JWT' \
-d 'token=保存的 refresh_token' \
-d 'token_type_hint=refresh_token'
成功时返回 200,没有响应体,令牌之前已经失效的也返回 200。没存 refresh token 的,可以用 access token 撤销,token_type_hint 改成 access_token。接口见 Apple 的[令牌撤销]。撤销后,用户下次再用「通过 Apple 登录」进你的 App,会像第一次一样重新授权姓名和邮箱。
用户也可能在 App 外面停用授权或删除 Apple 账户。在 App ID 的 Sign in with Apple 配置里登记一个服务器通知地址,Apple 会在这些事件发生时推送通知,事件类型包括 consent-revoked(停用授权)、account-deleted(删除 Apple 账户)、email-disabled 和 email-enabled(转发邮箱开关)。格式见 Apple 的[处理账户变更]。
用户选了隐藏邮箱、你又要给他发邮件的,要先在开发者后台登记发件域名或发件地址,并配好 SPF 或 DKIM,否则转发服务不会投递。
怎么在 iPhone 上测试通过 Apple 登录?
第一次登录
- 在 KXBuild 视图的「构建配置」节点选 Debug,运行
KXBuild: 构建并运行,装到手机上。调试方法见 [Windows 上真机调试 iOS App]。 - 点「通过 Apple 登录」按钮,系统弹出授权框,显示姓名和邮箱选项。可以改名字,邮箱可以选「共享我的电子邮件」或「隐藏邮件地址」。
- 用面容 ID、触控 ID 或密码确认。App 显示「已登录」和姓名、邮箱。
在代码里 signIn(with:) 的开头打一个断点,能看到 email、fullName 有值。服务端还没写好时,可以先把 loginURL 的请求注释掉,直接保存 user,把 App 端跑通。
再次登录
点「退出登录」后再点按钮,这次授权框只有一个「继续」,断点里 email 和 fullName 都是 nil。这是正常行为,说明第一次就要把它们存好。
撤销授权后重新测试第一次登录
想再看一次第一次授权的完整流程,按 Apple 的[管理使用「通过 Apple 登录」的 App]操作。
- 打开手机「设置」,轻点最上方你的姓名。
- 轻点「通过 Apple 登录」。
- 在列表里选中你的 App,轻点「删除」,按提示确认。
- 回到 App,App 回到前台时
checkCredentialState()拿到.revoked,自动退出登录。 - 再点登录按钮,授权框又会出现姓名和邮箱选项。
只是删掉 App 重装,不会重置授权,用户标识和「只给一次」的规则都不变。
Sign in with Apple 常见报错怎么处理?
| 现象 | 原因 | 处理 |
|---|---|---|
点按钮后报错 ASAuthorizationError 1000(unknown) | 签名里没有 com.apple.developer.applesignin,多见于 App ID 没开能力、描述文件没重新生成,或 target 没用上 entitlements 文件 | 双击 .mobileprovision 确认有这项能力,在工程设置编辑器里搜索 CODE_SIGN_ENTITLEMENTS 确认路径,重新构建 |
| 系统提示去「设置」里登录 Apple 账户 | 手机没登录 Apple 账户,或账户没开双重认证 | 在「设置」里登录 Apple 账户并开启双重认证后再试 |
| 报 1001(canceled) | 用户点了取消,或授权框被关掉 | 不用提示,让用户再点一次 |
| 报 1004(failed)、1002(invalidResponse) | 授权过程失败或响应异常 | 稍后重试,网络受限时换网络 |
构建报描述文件不含 com.apple.developer.applesignin | 描述文件是开启能力前生成的 | 编辑描述文件后保存,重新下载替换旧文件 |
安装报 signed with invalid entitlements | entitlements 里有这个键,描述文件里没有 | 同上,见[签名报错] |
email、fullName 是 nil | 不是第一次授权 | 按上文撤销授权后重测;正式 App 里从自己保存的数据读取 |
服务端验证 aud 不通过 | client_id 写成了 Services ID 或别的 Bundle ID | iOS App 登录时 aud 是 App 的 Bundle ID |
/auth/token 返回 invalid_client | client_secret 过期、签名错、sub 和 client_id 不一致,或 Key ID、Team ID 填错 | 重新生成 client_secret,核对 kid、iss、sub |
/auth/token 返回 invalid_grant | 授权码已用过或已过期 | 每次登录都用 App 新拿到的授权码,收到后立即换取 |
错误码数值以 SDK 里的 ASAuthorizationError.Code 为准,接口错误的含义见 Apple 的 [ErrorResponse]。Sign in with Apple 之外的签名和描述文件报错见 [iOS 签名报错与 ipa 安装失败]。
常见问题
App 有微信登录,必须加「通过 Apple 登录」吗?
按审核指南 4.8,用第三方或社交登录建立主账号的 App,要同时提供一种只收集姓名和邮箱、允许隐藏邮箱、不做广告追踪的登录方式。「通过 Apple 登录」满足这些条件,是最常用的做法;只用自家账号体系、企业或教育账号等情况可以不加。
为什么第二次登录拿不到邮箱和姓名?
Apple 只在用户第一次授权你的 App 时返回姓名和邮箱,之后的登录只返回用户标识和令牌。第一次拿到就要保存并传给服务端,identityToken 里之后仍带邮箱,但不带姓名。
测试时怎么重置「通过 Apple 登录」的授权?
在 iPhone 的「设置 → 你的姓名 → 通过 Apple 登录」里选中 App,轻点「删除」。之后再登录,系统会重新询问姓名和邮箱。
用户删除账号时要做什么?
在 App 内提供删除账号入口,服务端删除数据后,用保存的 refresh token 调用 https://appleid.apple.com/auth/revoke 撤销授权。Apple 要求使用「通过 Apple 登录」的 App 这样做。
ASAuthorizationError 1000 是什么原因?
1000 是 unknown 错误,最常见的原因是签名里缺少 com.apple.developer.applesignin:App ID 没开能力、描述文件没重新生成,或 target 没设置 entitlements 文件。签名都正确仍然报错时,删掉 App 后用新描述文件重新构建安装再试。
更多教程可以查看官网:https://www.kxbuild.net/zh/tutorial/201
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2501_91373349/article/details/170194892



