Atualmente, estou usando o seguinte código no meu cliente iOS para determinar se precisamos apresentar uma tela de login:
if Auth.auth().currentUser == nil
Aqui está a lógica da tela de login:
@objc func handleAppleSignUp() {
Analytics.logEvent("handleAppleSignUp", parameters: nil)
appleSignUpButton?.stopPulseAnimation()
startSignInWithAppleFlow()
}
//
// https://firebase.google.com/docs/auth/ios/apple
//
@available(iOS 13, *)
func startSignInWithAppleFlow() {
let nonce = randomNonceString()
currentNonce = nonce
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
var randomBytes = [UInt8](repeating: 0, count: length)
let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
if errorCode != errSecSuccess {
fatalError(
"Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
)
}
let charset: [Character] =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
let nonce = randomBytes.map { byte in
// Pick a random character from the set, wrapping around if needed.
charset[Int(byte) % charset.count]
}
return String(nonce)
}
@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
}
}
// https://fluffy.es/sign-in-with-apple-tutorial-ios/
extension LoginViewController: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
// Return the window of the current view controller
return self.view.window!
}
}
extension LoginViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.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 fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
// Initialize a Firebase credential, including the user's full name.
let credential = OAuthProvider.appleCredential(withIDToken: idTokenString,
rawNonce: nonce,
fullName: appleIDCredential.fullName)
EmulatorUtils.authUseEmulatorIfPossible()
// Sign in with Firebase.
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
// Error. If error.code == .MissingOrInvalidNonce, make sure
// you're sending the SHA256-hashed nonce as a hex string with
// your request to Apple.
print(error.localizedDescription)
return
}
// User is signed in to Firebase with Apple.
// ...
Analytics.logEvent("sign_in_success", parameters: nil)
self.delegate?.updateBasedOnLoginStatus()
}
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// Handle error.
print("Sign in with Apple errored: \(error)")
}
}
Eu estava pensando: será que precisamos atualizar o token de login manualmente? Alguns dos meus usuários relataram que as interações com o Firebase Functions e o Firestore às vezes falham. Em todos os casos, esse problema é resolvido saindo e entrando novamente.
Se eu precisar atualizar o token de login manualmente, alguém pode explicar como e quando fazer isso?
Se você estiver usando as bibliotecas de cliente do Firebase para qualquer plataforma compatível, isso acontece automaticamente. Você não precisa fazer nada.
Se a única maneira de verificar o status do usuário é com
currentUser
, provavelmente você está fazendo errado. Seu aplicativo deve usar um observador de estado de autenticação para saber com certeza quando um usuário está conectado ou desconectado.currentUser
não lida com o caso na primeira inicialização, antes que o SDK tenha tempo de validar o token do usuário. Quando seu aplicativo é iniciado recentemente,currentUser
sempre será nulo, antes que o SDK tenha a chance de atualizar qualquer token anterior, conforme necessário, mas o observador informará quando isso acontecer, e você poderá atualizar a interface do usuário do seu aplicativo para refletir isso.Leia também: Por que meu currentUser == null?