Firebase Apple Sign-In重新认证失败求助:删除用户遇重复凭证错误
解决Apple登录Firebase重新认证的凭证错误问题
问题根源
你当前的实现存在两个核心错误:
- 调用
user.getIDToken()获取的是Firebase自身的ID Token,并非Apple官方颁发的身份凭证,无法用于生成Apple OAuth认证所需的有效凭据 - 重新认证Apple登录用户必须触发用户再次通过系统授权弹窗验证身份,不能直接用现有Firebase凭证伪造Apple授权凭证
正确实现方案
重新认证Apple用户需要完整走一遍Apple的授权流程,获取新的Apple ID Token和对应随机nonce,再用这些信息生成有效的OAuth凭证,具体实现如下:
修改后的代码
// 生成随机nonce的工具方法(需和用户首次登录时使用的逻辑一致) extension String { static func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0..<16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError("生成nonce失败,SecRandomCopyBytes返回错误码:\(errorCode)") } return random } randoms.forEach { random in guard remainingLength > 0, random < charset.count else { return } result.append(charset[Int(random)]) remainingLength -= 1 } } return result } } func deleteAccountApple() async throws { guard let user = Auth.auth().currentUser else { throw AccountDeletionError.notSignedIn } do { try await user.delete() } catch { let nsError = error as NSError let authError = AuthErrorCode.Code(rawValue: nsError.code) if authError == .requiresRecentLogin { do { // 重新触发Apple授权流程获取有效凭证 let appleCredential = try await getAppleReauthenticationCredential() try await user.reauthenticate(with: appleCredential) try await user.delete() } catch { throw AccountDeletionError.failedToDeleteFirebaseAccount(error) } } else { throw AccountDeletionError.failedToDeleteFirebaseAccount(error) } } } private func getAppleReauthenticationCredential() async throws -> OAuthCredential { let rawNonce = String.randomNonceString() let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] // 必须将原始nonce做SHA256哈希后传给Apple request.nonce = sha256(rawNonce) return try await withCheckedThrowingContinuation { continuation in let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = AuthDelegate(continuation: continuation, rawNonce: rawNonce) // 需确保当前类实现ASAuthorizationControllerPresentationContextProviding协议 authorizationController.presentationContextProvider = self authorizationController.performRequests() } } // 处理Apple授权结果的代理类 private class AuthDelegate: NSObject, ASAuthorizationControllerDelegate { private let continuation: CheckedContinuation<OAuthCredential, Error> private let rawNonce: String init(continuation: CheckedContinuation<OAuthCredential, Error>, rawNonce: String) { self.continuation = continuation self.rawNonce = rawNonce super.init() } func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential, let appleIDToken = appleIDCredential.identityToken, let idTokenString = String(data: appleIDToken, encoding: .utf8) else { continuation.resume(throwing: AccountDeletionError.failedToGetAppleCredential) return } let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: rawNonce) continuation.resume(returning: credential) } func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { continuation.resume(throwing: error) } } // SHA256哈希工具方法 private func sha256(_ input: String) -> String { let inputData = input.data(using: .utf8)! let hashedData = SHA256.hash(data: inputData) return hashedData.compactMap { String(format: "%02x", $0) }.joined() } // 实现弹窗展示上下文协议(需在你的视图控制器类中添加) extension YourViewController: ASAuthorizationControllerPresentationContextProviding { func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { return self.view.window! } }
关键注意事项
- Nonce一致性:传给Apple的是SHA256哈希后的nonce,生成Firebase凭证时必须使用原始nonce,两者必须严格对应
- 强制用户授权:Apple要求重新认证必须用户手动确认,无法静默完成,因此必须通过
ASAuthorizationController触发系统授权弹窗 - 凭证来源正确:生成OAuth凭证时必须使用Apple返回的
identityToken,而非Firebase的ID Token
内容的提问来源于stack exchange,提问作者Sergey Zaycev
相关产品推荐
相关产品推荐

