using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using AppleAuth; using AppleAuth.Enums; using AppleAuth.Interfaces; using AppleAuth.Native; using Kill.Utils; using UnityEngine; #if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR using Firebase; using Firebase.Auth; #endif namespace Kill.Managers { /// /// Firebase 第三方登录管理器(支持 Google / Apple) /// public class FirebaseAuthManager : MonoBehaviour { public static FirebaseAuthManager Instance { get; private set; } // Google 登录配置 - 从 Firebase 控制台获取的 Web 客户端 ID private const string WEB_CLIENT_ID = "785438724947-kpjbqi43hbj6eddianbjsgkgkkclkfmd.apps.googleusercontent.com"; #if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR private FirebaseAuth auth; #endif private AppleAuthManager appleAuthManager; private void Awake() { Instance = this; InitializeFirebase(); InitializeAppleAuth(); } private void Update() { appleAuthManager?.Update(); } /// /// 初始化 Firebase /// private void InitializeFirebase() { #if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => { var dependencyStatus = task.Result; if (dependencyStatus == DependencyStatus.Available) { auth = FirebaseAuth.DefaultInstance; Debug.Log("Firebase 初始化成功"); } else { Debug.LogError($"Firebase 初始化失败: {dependencyStatus}"); } }); #else Debug.Log("Firebase 第三方登录仅在 Android / iOS 平台可用"); #endif } /// /// 初始化 Apple Sign In /// private void InitializeAppleAuth() { if (AppleAuthManager.IsCurrentPlatformSupported) { appleAuthManager = new AppleAuthManager(new PayloadDeserializer()); Debug.Log("Apple Sign In 初始化成功"); } else { Debug.Log("当前平台不支持 Apple Sign In"); } } /// /// 谷歌登录 /// public void SignInWithGoogle(Action onSuccess, Action onError, int timeoutSeconds = 30) { SignInWithProvider("google.com", new string[] { "email", "profile" }, onSuccess, onError, timeoutSeconds); } /// /// 苹果登录(使用 apple-signin-unity + Firebase OAuth Credential) /// public void SignInWithApple(Action onSuccess, Action onError, int timeoutSeconds = 30) { #if UNITY_IOS && !UNITY_EDITOR if (appleAuthManager == null) { onError?.Invoke("Apple Sign In 未初始化"); return; } if (!AppleAuthManager.IsCurrentPlatformSupported) { onError?.Invoke("当前平台不支持苹果登录"); return; } var rawNonce = GenerateRandomString(32); var nonce = GenerateSHA256NonceFromRawNonce(rawNonce); var loginArgs = new AppleAuthLoginArgs( LoginOptions.IncludeEmail | LoginOptions.IncludeFullName, nonce); appleAuthManager.LoginWithAppleId( loginArgs, credential => { var appleIdCredential = credential as IAppleIDCredential; if (appleIdCredential == null) { MainThread.Enqueue(() => onError?.Invoke("未获取到 Apple ID 凭证")); return; } var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken); var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode); if (string.IsNullOrEmpty(identityToken)) { MainThread.Enqueue(() => onError?.Invoke("Apple identity token 为空")); return; } SignInWithAppleCredential(appleIdCredential, identityToken, authorizationCode, rawNonce, onSuccess, onError); }, error => { MainThread.Enqueue(() => onError?.Invoke($"Apple 登录失败: {error.LocalizedDescription}")); }); #else onError?.Invoke("苹果登录仅在 iOS 平台可用"); #endif } #if UNITY_IOS && !UNITY_EDITOR /// /// 使用 Apple 凭证完成 Firebase 登录 /// private void SignInWithAppleCredential( IAppleIDCredential appleIdCredential, string identityToken, string authorizationCode, string rawNonce, Action onSuccess, Action onError) { if (auth == null) { onError?.Invoke("Firebase 未初始化"); return; } var firebaseCredential = OAuthProvider.GetCredential( "apple.com", identityToken, rawNonce, authorizationCode); auth.SignInWithCredentialAsync(firebaseCredential).ContinueWith(task => { if (task.IsCanceled) { MainThread.Enqueue(() => onError?.Invoke("Firebase 登录被取消")); return; } if (task.IsFaulted) { MainThread.Enqueue(() => onError?.Invoke("Firebase 登录失败")); return; } var user = auth.CurrentUser; if (user == null) { MainThread.Enqueue(() => onError?.Invoke("获取 Firebase 用户信息失败")); return; } MainThread.Enqueue(() => { var result = new GoogleLoginResult { FirebaseUserId = user.UserId, Email = user.Email ?? appleIdCredential.Email, DisplayName = user.DisplayName ?? GetAppleDisplayName(appleIdCredential.FullName), PhotoUrl = user.PhotoUrl?.ToString(), IdToken = "" }; onSuccess?.Invoke(result); }); }); } #endif /// /// 通用 OAuth 登录(Google) /// private void SignInWithProvider(string providerId, string[] scopes, Action onSuccess, Action onError, int timeoutSeconds) { #if UNITY_ANDROID && !UNITY_EDITOR if (auth == null) { onError?.Invoke("Firebase 未初始化"); return; } bool isCompleted = false; // 启动超时检测 Task.Run(async () => { await Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)); if (!isCompleted) { isCompleted = true; MainThread.Enqueue(() => onError?.Invoke($"{providerId} 登录超时({timeoutSeconds}秒)")); } }); var provider = new FederatedOAuthProviderData(providerId); provider.Scopes = scopes; var federatedProvider = new FederatedOAuthProvider(provider); auth.SignInWithProviderAsync(federatedProvider).ContinueWith(task => { if (isCompleted) return; // 已超时,忽略结果 isCompleted = true; if (task.IsFaulted) { MainThread.Enqueue(() => onError?.Invoke($"{providerId} 登录失败")); return; } if (task.IsCanceled) { MainThread.Enqueue(() => onError?.Invoke($"{providerId} 登录被取消")); return; } var user = auth.CurrentUser; if (user == null) { MainThread.Enqueue(() => onError?.Invoke("获取用户信息失败")); return; } // 构造登录结果 MainThread.Enqueue(() => { var result = new GoogleLoginResult { FirebaseUserId = user.UserId, Email = user.Email, DisplayName = user.DisplayName, PhotoUrl = user.PhotoUrl?.ToString(), IdToken = "" }; onSuccess?.Invoke(result); }); }); #else // 编辑器 / iOS 下谷歌登录不可用 Task.Delay(1000).ContinueWith(_ => { Debug.Log($"编辑器模拟 {providerId} 登录"); MainThread.Enqueue(() => { onError?.Invoke($"{providerId} 登录在当前平台不可用"); }); }); #endif } /// /// 登出 /// public void SignOut() { #if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR auth?.SignOut(); Debug.Log("Firebase 登出成功"); #endif } #region Nonce 生成 /// /// 生成随机字符串作为 raw nonce /// private static string GenerateRandomString(int length) { if (length <= 0) { throw new Exception("Expected nonce to have positive length"); } const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._"; using (var rng = new RNGCryptoServiceProvider()) { var result = new StringBuilder(); var randomNumberHolder = new byte[1]; while (result.Length < length) { var randomNumbers = new List(16); for (var i = 0; i < 16; i++) { rng.GetBytes(randomNumberHolder); randomNumbers.Add(randomNumberHolder[0]); } foreach (var randomNumber in randomNumbers) { if (result.Length >= length) break; if (randomNumber < charset.Length) { result.Append(charset[randomNumber]); } } } return result.ToString(); } } /// /// 计算 raw nonce 的 SHA256 值,传给 Apple /// private static string GenerateSHA256NonceFromRawNonce(string rawNonce) { using (var sha = SHA256.Create()) { var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(rawNonce)); var result = new StringBuilder(); for (var i = 0; i < hash.Length; i++) { result.Append(hash[i].ToString("x2")); } return result.ToString(); } } #endregion #region 辅助方法 /// /// 从 Apple 全名构造显示名称 /// private static string GetAppleDisplayName(IPersonName fullName) { if (fullName == null) return null; var parts = new List(); if (!string.IsNullOrEmpty(fullName.GivenName)) parts.Add(fullName.GivenName); if (!string.IsNullOrEmpty(fullName.FamilyName)) parts.Add(fullName.FamilyName); return parts.Count > 0 ? string.Join(" ", parts) : null; } #endregion } /// /// 第三方登录结果(兼容 Google / Apple) /// public class GoogleLoginResult { public string FirebaseUserId; // Firebase 用户ID(用于绑定) public string Email; // 邮箱 public string DisplayName; // 显示名称 public string PhotoUrl; // 头像URL public string IdToken; // ID Token } }