关于 authentication
为开发者提供使用 AuthenticationServices 等 API 实现 iOS 账户认证的指导,涵盖 Apple 登录、通行密钥、OAuth、密码自动填充和生物识别再认证,并强调凭据管理与服务端验证。
主要功能
- 01Apple 登录与凭据状态检查
- 02通行密钥注册和认证流程
- 03使用 ASWebAuthenticationSession 进行 OAuth
- 04密码自动填充凭据处理
- 05使用 LocalAuthentication 进行生物识别
- 06令牌验证与 Keychain 存储指导
适用场景
- 01为 iOS 登录流程添加 Apple 登录
- 02使用服务端挑战支持通行密钥
- 03通过第三方 OAuth 提供方进行认证
- 04账户敏感操作前重新验证身份
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill authentication安装后在 Claude Code 中输入/authentication确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/keychain-biometric.mdpasskeys.md详细介绍
身份验证
使用 AuthenticationServices 框架在 iOS 上实现身份验证流程,包括使用 Apple 登录、通行密钥、OAuth/第三方网页 身份验证、密码自动填充和生物识别重新验证。
目录
- 使用 Apple 登录
- 凭据处理
- 凭据状态检查
- 令牌验证
- 现有帐户设置流程
- 通行密钥
- ASWebAuthenticationSession(OAuth)
- 密码自动填充凭据
- 生物识别身份验证
- 安全边界
- SwiftUI SignInWithAppleButton
- 常见错误
- 审核清单
- 参考资料
使用 Apple 登录
使用这些 API 之前,先在 Xcode 中添加“使用 Apple 登录”功能。
UIKit:设置 ASAuthorizationController
import AuthenticationServices
final class LoginViewController: UIViewController {
func startSignInWithApple() {
let provider = ASAuthorizationAppleIDProvider()
let request = provider.createRequest()
request.requestedScopes = [.fullName, .email]
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
}
}
extension LoginViewController: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
view.window!
}
}
代理:处理成功和失败
extension LoginViewController: ASAuthorizationControllerDelegate {
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization
) {
guard let credential = authorization.credential
as? ASAuthorizationAppleIDCredential else { return }
let userID = credential.user // Stable, unique, per-team identifier
let email = credential.email // nil after first authorization
let fullName = credential.fullName // nil after first authorization
let identityToken = credential.identityToken // JWT for server validation
let authCode = credential.authorizationCode // Short-lived code for server exchange
// Save userID to Keychain for credential state checks
// See references/keychain-biometric.md for Keychain patterns
saveUserID(userID)
// Send identityToken and authCode to your server
authenticateWithServer(identityToken: identityToken, authCode: authCode)
}
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithError error: any Error
) {
switch (error as? ASAuthorizationError)?.code {
case .canceled, .notInteractive:
break
case .failed:
showError("Authorization failed")
default:
showError("Authorization failed: \(error.localizedDescription)")
}
}
}
凭据处理
| 凭据数据 | 必需的处理 |
|---|---|
user | 持久化此稳定的团队级标识符,以便检查凭据状态。 |
email, fullName | 这些可选值仅在首次授权时提供;请立即缓存。 |
identityToken, authorizationCode | 将它们发送到服务器进行验证或交换;切勿将其视为客户端身份验证凭据。 |
realUserStatus 只能作为防欺诈信号,不能作为身份验证凭据。
凭据状态检查
每次应用启动时都检查凭据状态。用户可以随时通过“设置”>“Apple 账户”>“登录与安全”撤销访问权限。
func checkCredentialState() {
let provider = ASAuthorizationAppleIDProvider()
guard let userID = loadSavedUserID() else {
showLoginScreen()
return
}
provider.getCredentialState(forUserID: userID) { state, _ in
DispatchQueue.main.async {
switch state {
case .authorized:
proceedToMainApp()
case .revoked:
// User revoked -- sign out and clear local data
signOut()
showLoginScreen()
case .notFound:
showLoginScreen()
case .transferred:
// App transferred to new team -- migrate user identifier
migrateUser()
@unknown default:
showLoginScreen()
}
}
}
}
凭据撤销通知
NotificationCenter.default.addObserver(
forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification,
object: nil,
queue: .main
) { _ in
// Sign out immediately
AuthManager.shared.signOut()
}
令牌验证
identityToken 是一个 JWT。将其发送到服务器进行验证——
切勿仅在客户端信任它。
在服务器端,根据 Apple 的公钥
https://appleid.apple.com/auth/keys(JWKS)验证 JWT。确认:iss 为
https://appleid.apple.com,aud 与你的 bundle ID 匹配,且 exp 尚未
过期。在服务器上交换短期有效的授权码,并将生成的应用会话令牌存储在 Keychain 中。
现有帐户设置流程
应用启动时,在显示登录界面之前,静默检查现有的“使用 Apple 登录”凭据和密码 凭据:
func performExistingAccountSetupFlows() {
let appleIDRequest = ASAuthorizationAppleIDProvider().createRequest()
let passwordRequest = ASAuthorizationPasswordProvider().createRequest()
let controller = ASAuthorizationController(
authorizationRequests: [appleIDRequest, passwordRequest]
)
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests(
options: .preferImmediatelyAvailableCredentials
)
}
在 viewDidAppear 中或应用启动时调用此方法。如果未找到现有凭据,代理会收到 .notInteractive 错误——请静默处理该错误,并显示常规登录界面。
通行密钥
仅对已配置 webcredentials: 关联域和 AASA 条目的依赖方域使用通行密钥。注册和断言都需要新的服务器质询、ASAuthorizationPlatformPublicKeyCredentialProvider、具有活动展示锚点的授权控制器,以及在发放会话前进行服务器端验证。
加载 references/passkeys.md,查看规范的注册、断言、结果处理、自动填充辅助和实体安全密钥流程。
ASWebAuthenticationSession(OAuth)
使用 ASWebAuthenticationSession 进行 OAuth 和第三方身份验证
(Google、GitHub 等)。切勿使用 WKWebView 处理身份验证流程。
import AuthenticationServices
final class OAuthController: NSObject, ASWebAuthenticationPresentationContextProviding {
private weak var presentationAnchor: ASPresentationAnchor?
init(presentationAnchor: ASPresentationAnchor) {
self.presentationAnchor = presentationAnchor
}
func startOAuthFlow() {
let authURL = URL(string:
"https://provider.com/oauth/authorize?client_id=YOUR_ID&redirect_uri=myapp://callback&response_type=code"
)!
let session = ASWebAuthenticationSession(
url: authURL, callback: .customScheme("myapp")
) { callbackURL, error in
guard let callbackURL, error == nil,
let code = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false)?
.queryItems?.first(where: { $0.name == "code" })?.value else { return }
Task { await self.exchangeCodeForTokens(code) }
}
session.presentationContextProvider = self
session.prefersEphemeralWebBrowserSession = true // No shared cookies
session.start()
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let presentationAnchor else {
fatalError("ASWebAuthenticationSession needs the active window")
}
return presentationAnchor
}
}
在 SwiftUI 中,使用 @Environment(\.webAuthenticationSession),并通过 .customScheme("myapp") 或 .https(host:path:) 调用 authenticate(using:callback:preferredBrowserSession:additionalHeaderFields:);只有在提供方流程需要避免使用共享浏览器 Cookie 时,才优先选择 .ephemeral。
密码自动填充凭据
使用 现有帐户设置流程 中的单个控制器,在“使用 Apple 登录”的同时提供 ASAuthorizationPasswordProvider。在该控制器的代理中处理 ASPasswordCredential。
在文本字段上设置 textContentType,以启用自动填充:
usernameField.textContentType = .username
passwordField.textContentType = .password
生物识别认证
使用 LocalAuthentication 中的 LAContext,在显示账户设置或启动敏感操作前进行本地重新认证。不要将返回的
Bool 视为解锁已存储机密信息的凭据;应改用 Keychain 访问控制保护机密信息。请参阅
references/keychain-biometric.md,了解规范的 LAContext、回退、
SecAccessControl 和 .biometryCurrentSet 用法。
**必需:**在 Info.plist 中添加 NSFaceIDUsageDescription。缺少此键会导致 Face ID 设备崩溃。
安全边界
此技能负责面向用户的账户认证:使用 Apple 登录、通行密钥、密码自动填充、ASAuthorizationController、OAuth 会话展示、凭据状态和本地生物识别重新认证。深层安全工作应交由 swift-security 处理:Keychain 架构/迁移、CryptoKit、Secure Enclave、证书固定/信任、Keychain 共享、存储加固,以及 OWASP MASVS/MASTG。此处仅保留最低限度的存储要求:令牌和机密信息应存放在 Keychain 中;单独调用 LAContext.evaluatePolicy 不得释放受保护的机密信息。
SwiftUI SignInWithAppleButton
当登录界面使用 SwiftUI 时,在 SwiftUI 视图中使用 SignInWithAppleButton。请求 .fullName 和 .email,将成功结果向下转型为
ASAuthorizationAppleIDCredential,并传递给共享的
令牌验证流程。使用
.signInWithAppleButtonStyle(...) 设置样式。
常见错误
- 认为已保存本地会话就表示 Apple ID 凭据仍然有效。在应用启动时检查凭据状态,并处理撤销通知。
- 未尝试现有账户设置流程就显示完整登录界面。将
.notInteractive视为正常的“无本地凭据”路径。 - 对
email或fullName使用强制解包。首次授权时缓存它们,并处理之后出现的nil。 - 创建
ASAuthorizationController时未提供展示上下文代理。授权界面需要当前活动的展示锚点。 - 将身份令牌、授权码、访问令牌、密码或通行密钥服务器状态存储在
UserDefaults、文件或 Core Data 中。机密信息应存放在 Keychain 中;依赖方的通行密钥验证应保留在服务器端。 - 添加通行密钥请求时未为依赖方域配置
webcredentials:Associated Domains,或尝试将应用原生通行密钥用于无关的网站。 - 将认证工作扩展到 CryptoKit、Secure Enclave、证书固定或 OWASP MASVS。应将这些工作交由
swift-security处理。
审核清单
- 已在 Xcode 项目中添加“使用 Apple 登录”功能
- 已实现
ASAuthorizationControllerPresentationContextProviding - 每次应用启动时均检查凭据状态(
getCredentialState(forUserID:completion:)) - 已注册
credentialRevokedNotification观察器;已处理登出 - 首次授权时已缓存
email和fullName(不假设之后仍可获取) - 将
identityToken发送到服务器进行验证,不仅在客户端信任 - 令牌存储在 Keychain 中,而非 UserDefaults 或文件中
- 在显示登录界面前已调用
performExistingAccountSetupFlows - 已处理错误情况:
.canceled、.failed、.notInteractive - 生物识别认证所需的
NSFaceIDUsageDescription已添加到 Info.plist - OAuth 使用
ASWebAuthenticationSession(而非WKWebView) - 在适当情况下为 OAuth 设置
prefersEphemeralWebBrowserSession - 已为用户名/密码字段设置
textContentType,以支持自动填充 - 通行密钥依赖方已配置
webcredentials:Associated Domains - 通行密钥注册/断言质询由服务器提供,并在服务器端验证
- 深层 Keychain、CryptoKit、Secure Enclave、证书固定和 MASVS 工作已交由
swift-security处理
参考资料
- Keychain 与生物识别模式:references/keychain-biometric.md
- Passkey 模式:references/passkeys.md
- AuthenticationServices
- ASAuthorizationAppleIDProvider
- ASAuthorizationAppleIDCredential
- ASAuthorizationController
- ASWebAuthenticationSession
- 支持 passkey
- ASAuthorizationPlatformPublicKeyCredentialProvider
- ASAuthorizationPasswordProvider
- SignInWithAppleButton
- 使用 Apple 登录实现用户身份验证