Fornire diverse modalità di accesso è fondamentale per massimizzare il tasso di conversione in fase di onboarding su un'app iOS. Le linee guida di Apple (App Store Review Guidelines 4.8) impongono che se un'app offre l'accesso con terze parti (come Google o Facebook), deve obbligatoriamente offrire anche il Sign-In con Apple.
In questo articolo analizziamo la struttura completa di un gestore di autenticazione multimodale in SwiftUI collegato ad un backend Laravel Sanctum su VPS, coprendo Apple Sign-In, Google OAuth2, Login con Email/Password e la modalità Guest Mode (Esploratore Anonimo).
1. Architettura dell'Auth Manager in SwiftUI
L'autenticazione deve essere gestita da un singleton conforme al protocollo ObservableObject o al nuovo macro @Observable di Swift 5.9, con persistenza sicura dei token JWT all'interno dell'iOS Keychain.
import SwiftUI
import AuthenticationServices
@MainActor
public final class EchoSocialAuthManager: ObservableObject {
public static let shared = EchoSocialAuthManager()
@Published public var isAuthenticated: Bool = false
@Published public var isGuest: Bool = false
@Published public var currentUser: UserProfile?
@Published public var authToken: String?
private init() {
loadStoredSession()
}
public func loadStoredSession() {
if let token = KeychainHelper.standard.read(service: "bitre-auth", account: "sanctum-token") {
self.authToken = token
self.isAuthenticated = true
Task { await fetchCurrentProfile() }
}
}
public func setSession(token: String, user: UserProfile) {
KeychainHelper.standard.save(token, service: "bitre-auth", account: "sanctum-token")
self.authToken = token
self.currentUser = user
self.isAuthenticated = true
self.isGuest = false
}
public func enableGuestMode() {
self.isGuest = true
self.isAuthenticated = true
self.currentUser = UserProfile.guestUser()
}
public func logout() {
KeychainHelper.standard.delete(service: "bitre-auth", account: "sanctum-token")
self.authToken = nil
self.currentUser = nil
self.isAuthenticated = false
self.isGuest = false
}
}
2. Integrazione Native Sign-In con Apple
Con il framework AuthenticationServices, puoi integrare il pulsante nativo Apple in pochi righi di codice in SwiftUI:
SignInWithAppleButton(.signIn) { request in
request.requestedScopes = [.fullName, .email]
} onCompletion: { result in
switch result {
case .success(let authorization):
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
let userIdentifier = appleIDCredential.user
let email = appleIDCredential.email ?? ""
let fullName = [appleIDCredential.fullName?.givenName, appleIDCredential.fullName?.familyName]
.compactMap { $0 }.joined(separator: " ")
guard let identityTokenData = appleIDCredential.identityToken,
let identityTokenString = String(data: identityTokenData, encoding: .utf8) else { return }
Task {
await loginWithBackendApple(appleId: userIdentifier, email: email, name: fullName, token: identityTokenString)
}
}
case .failure(let error):
print("Apple Auth Error: \(error.localizedDescription)")
}
}
.signInWithAppleButtonStyle(.whiteOutline)
.frame(height: 50)
.cornerRadius(12)
3. Sanificazione del Backend Laravel per Apple Private Relay
Quando l'utente seleziona "Nascondi la mia email", Apple genera un indirizzo anonimo del tipo xyz123@privaterelay.appleid.com. Inoltre, durante i login successivi al primo, Apple non reinvia il nome e l'email. Il controller Laravel deve sanificare le stringhe vuote ed evitare di sovrascrivere l'email già salvata:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class AuthController extends Controller
{
public function appleAuth(Request $request)
{
$validated = $request->validate([
'apple_id' => 'required|string',
'email' => 'nullable|string',
'name' => 'nullable|string',
]);
$appleId = $validated['apple_id'];
$rawEmail = trim($validated['email'] ?? '');
$rawName = trim($validated['name'] ?? '');
// Cerca utente esistente tramite apple_id
$user = User::where('apple_id', $appleId)->first();
if (!$user) {
$email = !empty($rawEmail) ? $rawEmail : "apple_" . Str::random(8) . "@privaterelay.appleid.com";
$name = !empty($rawName) ? $rawName : "Apple Explorer";
$handle = Str::slug($name) . "_" . Str::random(4);
$user = User::create([
'apple_id' => $appleId,
'name' => $name,
'email' => $email,
'handle' => $handle,
'password' => bcrypt(Str::random(32)),
]);
}
$token = $user->createToken('ios-device')->plainTextToken;
return response()->json([
'status' => 'success',
'token' => $token,
'user' => $user
]);
}
}
4. Gestione Sicura Keychain in iOS
I token JWT non dovrebbero mai essere memorizzati in UserDefaults perché non sono crittografati. Utilizza la Security API nativa di iOS per memorizzare le credenziali nel Keychain crittografato dal chip Secure Enclave del dispositivo.
Hai un'idea di App iOS o desideri consulenza tecnica per la tua infrastruttura Cloud VPS?
Sviluppo applicazioni iOS native in SwiftUI, backend Laravel ad alte prestazioni e supporto per monetizzazione internazionale.