繁体   English   中英

Firebase 身份验证 - 使用 Apple 登录:nonce 返回 nil

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM