Аутентификация Firebase — вход с помощью Apple: одноразовый номер возвращает ноль
Странный. Я явно что-то упускаю. я устанавливаю на я создаю изrandomNonceString
метод.
The handleSignInWithAppleCompletion(_:)
не терпит неудачу. Это удается, но вылетает с фатальной ошибкой, как и хотелось бы, если бы у меня было Invalid State , т.е. запрос на вход не был отправлен. Мойnonce
даже не создан, поэтому мойcurrentNonce
, конечно, ноль.
Почему?
Вот мой код:
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)")
}
}
}
}
}
}
Неустранимая ошибка: недопустимое состояние: обратный вызов для входа был получен, но запрос на вход не был отправлен.
Что вы пробовали и чего ожидали?
• Перепроверка кода • Отладка в консоли • Включение музыки погромче
ОБНОВЛЯТЬ
Я попытался сознательно создать запрос на основе документации 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()
}
}
2 ответа
Код выглядит в основном хорошо (похоже, он основан на образце приложения , которое идет вместе с видео о начале работы со входом в Apple с использованием аутентификации Firebase на платформах Apple, которое я недавно опубликовал).
Однако есть небольшая, но существенная опечатка: вместо ссылки наsignInManager
имущество внутриSignInWithAppleButton
обработчики завершения, вы создаете экземпляр нового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)
}
Ну, это заняло целую вечность.
Решение очевидно задним числом, но я слишком рабски следовал документам. Всегда узнавайте, что документы пытаются донести о том, как работает программное обеспечение, а не доверяйте документации.
Мне просто нужно было инициализироватьcurrentNonce
До:fileprivate var currentNonce: ?
После:fileprivate var currentNonce = String()