简体   繁体   English

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

[英]Firebase Authentication - Sign In with Apple: nonce returns nil

Strange.奇怪的。 I'm clearly missing something.我显然错过了一些东西。 I'm setting currentNonce to the nonce I'm creating from the randomNonceString method.我将currentNonce设置为我从randomNonceString方法创建的nonce

The handleSignInWithAppleCompletion(_:) doesn't fail. handleSignInWithAppleCompletion(_:)不会失败。 It succeeds, but crashes with the fatal error, as I would like it to if I have an Invalid State , ie no login request was sent.它成功了,但因致命错误而崩溃,如果我有一个Invalid State ,即没有发送登录请求,我希望如此。 My nonce is not even instantiated, so my currentNonce , of course, is nil.我的nonce甚至没有被实例化,所以我的currentNonce当然是 nil。

Why?为什么?

Here's my code:这是我的代码:

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)")
                    }
                }
            }
        }
    }
}

Fatal error: Invalid state: a login callback was received, but no login request was sent.致命错误:无效 state:收到登录回调,但未发送登录请求。

What did you try and what were you expecting?你尝试了什么,你期待什么?

• Rechecking my code • Debugging in console • Turning the music up • 重新检查我的代码 • 在控制台中调试 • 调高音乐

UPDATE更新

I attempted to deliberately create a request based on Firebase's documentation :我试图根据 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)
    }

The line let request = appleIDProvider.createRequest() changed nothing. let request = appleIDProvider.createRequest()这一行没有任何改变。 Still working on solutions.仍在研究解决方案。

Adding SignInButtonView添加 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()
    }
}

Well, this took forever.好吧,这花了很长时间。

The solution is obvious in hindsight, but I was following the docs too slavishly.事后看来,解决方案是显而易见的,但我过于盲目地遵循文档。 Always learn what the docs are trying to get across about how the software works versus trusting the documentation.始终了解文档试图传达的关于软件如何工作与信任文档的内容。

I just needed to initialize the currentNonce我只需要初始化currentNonce

Before : fileprivate var currentNonce: ?之前fileprivate var currentNonce: ? After : fileprivate var currentNonce = String()之后fileprivate var currentNonce = String()

The code looks mostly fine (it seems like it is based on the sample app that goes along with the video about Getting started with sign in with Apple using Firebase Authentication on Apple platforms I recently published).代码看起来大部分都很好(它似乎是基于示例应用程序,该示例应用程序与我最近发布的有关在 Apple 平台上使用 Firebase 身份验证开始使用 Apple 登录的视频一起发布)。

However, there is a small, but significant typo: instead of referring to the signInManager property inside the SignInWithAppleButton completion handlers, you instantiate a new SignInManager every time you receive a callback.但是,有一个小而重要的拼写错误:您没有在SignInWithAppleButton完成处理程序中引用signInManager属性,而是在每次收到回调时实例化一个新的SignInManager

This results in currentNonce being nil whenever you receive the second callback.这会导致currentNonce在您收到第二个回调时为nil

Change this:改变这个:

SignInWithAppleButton { request in
  SignInManager().handleSignInWithAppleRequest(request)
} onCompletion: { result in
  SignInManager().handleSignInWithAppleCompletion(result)
}

to this:对此:

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