Per soddisfare le App Store Review Guidelines (Sezione 4.8), ogni app iOS che integra sistemi di login terzi deve offrire Sign in with Apple. Integrare Sign in with Apple con Firebase Auth consente di beneficiare della sicurezza nativa Apple combinata con la flessibilità del token JWT gestito da Firebase.
1. Configurazione in SwiftUI con AuthenticationServices
In SwiftUI possiamo utilizzare il componente nativo SignInWithAppleButton e scambiare il token di identità ID con Firebase Auth in maniera del tutto trasparente.
import SwiftUI
import AuthenticationServices
import FirebaseAuth
import CryptoKit
struct AppleSignInView: View {
@State private var currentNonce: String?
var body: some View {
SignInWithAppleButton(.signIn) { request in
let nonce = randomNonceString()
currentNonce = nonce
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
} onCompletion: { result in
switch result {
case .success(let authorization):
guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential,
let appleIDToken = appleIDCredential.identityToken,
let idTokenString = String(data: appleIDToken, encoding: .utf8),
let nonce = currentNonce else { return }
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
rawNonce: nonce)
Auth.auth().signIn(with: credential) { authResult, error in
if let error = error {
print("Errore login Firebase Auth: \(error.localizedDescription)")
return
}
print("Login completato con successo per UID: \(authResult?.user.uid ?? "")")
}
case .failure(let error):
print("Errore Sign in with Apple: \(error.localizedDescription)")
}
}
.frame(height: 50)
.padding()
}
private func randomNonceString(length: Int = 32) -> String {
var randomBytes = [UInt8](repeating: 0, count: length)
_ = SecRandomCopyBytes(kSecRandomDefault, length, &randomBytes)
let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
return String(randomBytes.map { charset[Int($0) % charset.count] })
}
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
return hashedData.compactMap { String(format: "%02x", $0) }.joined()
}
}
2. Decodifica e Lettura dei Custom Claims in Swift
Una volta autenticato l'utente, i Custom Claims assegnati dal backend serverless (es. ruoli `admin`, `premium`) possono essere verificati nel client forzando l'aggiornamento del token ID:
func checkUserPermissions() {
guard let user = Auth.auth().currentUser else { return }
user.getIDTokenResult(forcingRefresh: true) { result, error in
if let claims = result?.claims, let isPremium = claims["isPremium"] as? Bool, isPremium {
print("Accesso Funzionalità Premium Accordato!")
}
}
}