![](/img/trans.png)
[英]iOS app crashes when trying to "Sign in with Apple" using Firebase Authentication
[英]Firebase Authentication - Sign In with Apple: nonce returns nil
奇怪的。 我显然错过了一些东西。 我将currentNonce
设置为我从randomNonceString
方法创建的nonce
。
handleSignInWithAppleCompletion(_:)
不会失败。 它成功了,但因致命错误而崩溃,如果我有一个Invalid State ,即没有发送登录请求,我希望如此。 我的nonce
甚至没有被实例化,所以我的currentNonce
当然是 nil。
为什么?
这是我的代码:
import SwiftUI
import LocalAuthentication
import FirebaseAuth
import CryptoKit
import _AuthenticationServices_SwiftUI
final class SignInManager: ObservableObject {
@Published var errorMessage = ""
private var currentNonce: String?
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
private 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(
"Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
)
}
return random
}
randoms.forEach { random in
if remainingLength == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
@available(iOS 13, *)
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
String(format: "%02x", $0)
}.joined()
return hashString
}
}
extension SignInManager {
func handleSignInWithAppleRequest(_ request: ASAuthorizationAppleIDRequest) {
request.requestedScopes = [.fullName, .email]
let nonce = randomNonceString()
currentNonce = nonce
request.nonce = sha256(nonce)
}
func handleSignInWithAppleCompletion(_ result: Result<ASAuthorization, Error>) {
if case .failure(let failure) = result {
errorMessage = failure.localizedDescription
}
else if case .success(let success) = result {
if let appleIDCredential = success.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
fatalError("Invalid state: a login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetdch identify token.")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
rawNonce: nonce)
Task {
do {
let result = try await Auth.auth().signIn(with: credential)
await updateDisplayName(for: result.user, with: appleIDCredential)
}
catch {
print("Error authenticating: \(error.localizedDescription)")
}
}
}
}
}
}
致命错误:无效 state:收到登录回调,但未发送登录请求。
你尝试了什么,你期待什么?
• 重新检查我的代码 • 在控制台中调试 • 调高音乐
更新
我试图根据 Firebase 的文档故意创建一个请求:
func handleSignInWithAppleRequest(_ request: ASAuthorizationAppleIDRequest) {
let nonce = randomNonceString()
currentNonce = nonce
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
}
let request = appleIDProvider.createRequest()
这一行没有任何改变。 仍在研究解决方案。
添加 SignInButtonView
import SwiftUI
import FirebaseAuth
import AuthenticationServices
struct SignInButtonView: View {
@EnvironmentObject var signInManager: SignInManager
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
// MARK: - Sign In With Apple
HStack {
SignInWithAppleButton { request in
SignInManager().handleSignInWithAppleRequest(request)
} onCompletion: { result in
SignInManager().handleSignInWithAppleCompletion(result)
}
.signInWithAppleButtonStyle(colorScheme == .light ? .black : .white)
.frame(maxWidth: .infinity, maxHeight: 50)
.cornerRadius(8)
}
.padding()
}
.frame(width: 400)
}
}
struct SignInButtonView_Previews: PreviewProvider {
static var previews: some View {
SignInButtonView()
}
}
好吧,这花了很长时间。
事后看来,解决方案是显而易见的,但我过于盲目地遵循文档。 始终了解文档试图传达的关于软件如何工作与信任文档的内容。
我只需要初始化currentNonce
之前: fileprivate var currentNonce: ?
之后: fileprivate var currentNonce = String()
代码看起来大部分都很好(它似乎是基于示例应用程序,该示例应用程序与我最近发布的有关在 Apple 平台上使用 Firebase 身份验证开始使用 Apple 登录的视频一起发布)。
但是,有一个小而重要的拼写错误:您没有在SignInWithAppleButton
完成处理程序中引用signInManager
属性,而是在每次收到回调时实例化一个新的SignInManager
。
这会导致currentNonce
在您收到第二个回调时为nil
。
改变这个:
SignInWithAppleButton { request in
SignInManager().handleSignInWithAppleRequest(request)
} onCompletion: { result in
SignInManager().handleSignInWithAppleCompletion(result)
}
对此:
SignInWithAppleButton { request in
signInManager.handleSignInWithAppleRequest(request)
} onCompletion: { result in
signInManager.handleSignInWithAppleCompletion(result)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.