using System; using System.Text.RegularExpressions; using System.Threading.Tasks; using AuthenticationModule.Runtime; using Newtonsoft.Json; using Unity.Services.Authentication; using Unity.Services.Core; using UnityEngine; namespace Phantom.XRMOD.GameServices.Runtime { /// /// Manages the authentication system, providing various sign-in methods and user information management. /// Wraps Unity's Authentication Service. /// public class AuthenticationSystemManager { /// /// Invoked when the user successfully signs in. /// public static event Action SignedInCallback; /// /// Invoked when the user signs out. /// public static event Action SignOutCallback; /// /// Invoked when a sign-in attempt fails. /// public static event Action SignFailedCallback; /// /// Invoked when a sign-in code is received (e.g., for device code flow). /// public static event Action SignInCodeReceivedCallback; /// /// Invoked when a sign-in code expires. /// public static event Action SignInCodeExpiredCallback; /// /// Invoked when the current session/token has expired. /// public static event Action ExpiredCallback; private static bool isSiginning; private static UserInfoModel _USER_INFO_MODEL = new(); /// /// Initializes the authentication service callbacks. /// This should be called once the Unity Services are initialized. /// public static void InitServices() { // AuthenticationService.SignedIn execute before got the user data // AuthenticationService.Instance.SignedIn += SignedInCallback; AuthenticationService.Instance.SignedOut += SignOutCallback; AuthenticationService.Instance.SignInFailed += SignFailedCallback; AuthenticationService.Instance.SignInCodeExpired += SignInCodeExpiredCallback; AuthenticationService.Instance.SignInCodeReceived += SignInCodeReceivedCallback; AuthenticationService.Instance.Expired += ExpiredCallback; } #region Custom UserName and Password APIs /// /// Signs in a user using a username and password. /// /// The username. /// The password. /// Callback invoked on error with the error message. /// A Task representing the sign-in process. /// /// /// await AuthenticationSystemManager.SignInUserNameWithPassword("myUser", "myPass", (err) => Debug.LogError(err)); /// /// public static async Task SignInUserNameWithPassword(string _userName, string _pwd, Action _error) { try { await AuthenticationService.Instance.SignInWithUsernamePasswordAsync(_userName, _pwd); } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogError($"Authentication:{tmp_Ex}"); _error?.Invoke(tmp_Ex.Message); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogError(tmp_Ex); _error?.Invoke(tmp_Ex.Message); } } /// /// Updates the current user's password. /// /// The current password. /// The new password. /// A Task representing the update process. public static async Task UpdateUserNamePassword(string _currentPwd, string _newPwd) { try { await AuthenticationService.Instance.UpdatePasswordAsync(_currentPwd, _newPwd); } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } } /// /// Signs up a new user with a username and password. /// /// The username. /// The password. /// Callback invoked on error with the error message. /// A Task representing the sign-up process. public static async Task SignUpUserNameWithPassword(string _userName, string _newPwd, Action _error) { try { await AuthenticationService.Instance.SignUpWithUsernamePasswordAsync(_userName, _newPwd); } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); _error?.Invoke(tmp_Ex.Message); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); _error?.Invoke(tmp_Ex.Message); } } #endregion #region Sign with Platforms /// /// Signs in using Apple Authentication. /// Requires the USE_APPLE_AUTHENTICATION define. /// public static void SignInWithApple() { #if USE_APPLE_AUTHENTICATION var tmp_AppleIdSignInProvider = PlatformProviderFactory.GetPlatformProvider(PlatformProviderType.Apple); tmp_AppleIdSignInProvider.Initialize(); tmp_AppleIdSignInProvider.SignIn(_userInfo => { _USER_INFO_MODEL = _userInfo; SignedInCallback?.Invoke(); }, Debug.LogError); #endif } public static async void SignInWithAppIdToken(string _idToken) { var tmp_Credential = new UserInfoModel { token = _idToken }; try { await AuthenticationService.Instance.SignInWithAppleAsync(tmp_Credential.token); if (string.IsNullOrEmpty(tmp_Credential.player_name)) { var tmp_PlayerName = await AuthenticationService.Instance.GetPlayerNameAsync(); tmp_Credential.player_name = tmp_PlayerName; } // Cloud save user information to cloud if (!string.IsNullOrEmpty(tmp_Credential.email)) { tmp_Credential.player_name = AuthenticationService.Instance.PlayerInfo.Username; var tmp_UserInfoDict = new System.Collections.Generic.Dictionary() { {"UserInformation", tmp_Credential} }; await Unity.Services.CloudSave.CloudSaveService.Instance.Data.Player.SaveAsync(tmp_UserInfoDict); } else { var tmp_Result = await Unity.Services.CloudSave.CloudSaveService.Instance.Data.Player.LoadAsync( new System.Collections.Generic.HashSet {"UserInformation"}); if (tmp_Result.TryGetValue("UserInformation", out var tmp_Item)) { tmp_Credential = tmp_Item.Value.GetAs(); } } SignedInCallback?.Invoke(); // Stop Apple authentication checking } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } } /// /// Sign in with google account /// public static void SignInWithGooglePlay() { #if UNITY_ANDROID && USE_GOOGLE_AUTHENTICATION IPlatformSignInProvider tmp_PlatformSignInProvider = new SingInWithGooglePlayProvider(); tmp_PlatformSignInProvider.SignIn( async _credential => { try { await AuthenticationService.Instance.SignInWithGoogleAsync(_credential.token); var tmp_PlayerName = await AuthenticationService.Instance.GetPlayerNameAsync(false); _USER_INFO_MODEL = _credential; SignedInCallback?.Invoke(); } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } }, Debug.LogError); #endif } /// /// Sign in with meta account /// public static void SignInWithMeta() { #if USE_META_AUTHENTICATION IPlatformSignInProvider tmp_SignInProvider = new SignInWithMetaProvider(); tmp_SignInProvider.Initialize(() => { tmp_SignInProvider.SignIn(async _credential => { try { await AuthenticationService.Instance.SignInWithOculusAsync(_credential.token, _credential.accountId); if (!string.IsNullOrEmpty(_credential.player_name)) { await AuthenticationService.Instance.UpdatePlayerNameAsync(_credential.player_name); } _USER_INFO_MODEL = _credential; SignedInCallback?.Invoke(); } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } }, Debug.LogError); }); #endif } /// /// Sign in with rokid account /// /// Rokid platform client id /// Rokid platform client secret public static void SignInWithRokid(string _clientId, string _clientSecret) { #if USE_ROKID_AUTHENTICATION var tmp_RokidSignInProvider = PlatformProviderFactory.GetPlatformProvider(PlatformProviderType.Rokid); tmp_RokidSignInProvider.Initialize(); tmp_RokidSignInProvider.SignIn(async _userInfo => { string tmp_UserName; if (!string.IsNullOrEmpty(_userInfo.email)) tmp_UserName = $"n{_userInfo.email.Split("@")[0]}"; else if (!string.IsNullOrEmpty(_userInfo.mobile)) tmp_UserName = $"n{_userInfo.mobile}"; else tmp_UserName = $"n{_userInfo.accountId.Substring(0, 11)}"; bool tmp_IsExists = false; await SignUpUserNameWithPassword(tmp_UserName, _userInfo.token, async _error => { if (!_error.Contains("exists")) return; await SignInUserNameWithPassword(tmp_UserName, _userInfo.token, Debug.LogError); if (!string.IsNullOrEmpty(_userInfo.player_name)) await AuthenticationService.Instance.UpdatePlayerNameAsync(_userInfo.player_name); SignedInCallback?.Invoke(); }); }, Debug.LogError); #endif } #endregion /// /// Signs in using a cached session token if it exists. /// A cached player exists if they previously signed in and the token hasn't been deleted. /// /// A Task representing the sign-in process. public static async Task SignInWithCache() { if (!AuthenticationService.Instance.SessionTokenExists) return; if (AuthenticationService.Instance.IsSignedIn) return; if (AuthenticationService.Instance.IsAuthorized) return; if (isSiginning) return; isSiginning = true; try { await AuthenticationService.Instance.SignInAnonymouslyAsync(); isSiginning = false; _USER_INFO_MODEL = new UserInfoModel { token = AuthenticationService.Instance.AccessToken, player_name = AuthenticationService.Instance.PlayerName, email = string.Empty, user_avatar = string.Empty }; SignedInCallback?.Invoke(); } catch (AuthenticationException tmp_Ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } catch (RequestFailedException tmp_Ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(tmp_Ex); } } /// /// Signs in anonymously. /// /// A Task representing the sign-in process. public static async Task SignInWithAnonymous() { try { await AuthenticationService.Instance.SignInAnonymouslyAsync(); var tmp_PlayerName = await AuthenticationService.Instance.GetPlayerNameAsync(true); _USER_INFO_MODEL = new UserInfoModel() { email = String.Empty, user_avatar = string.Empty, player_name = tmp_PlayerName }; SignedInCallback?.Invoke(); } catch (AuthenticationException ex) { // Compare error code to AuthenticationErrorCodes // Notify the player with the proper error message Debug.LogException(ex); } catch (RequestFailedException ex) { // Compare error code to CommonErrorCodes // Notify the player with the proper error message Debug.LogException(ex); } } /// /// Signs out the current user and clears the session token. /// /// Optional profile name (currently unused in this method but provided for context). public static void SignOut(string _profileName = null) { if (AuthenticationService.Instance.IsAuthorized || AuthenticationService.Instance.IsSignedIn) { _USER_INFO_MODEL.CleanUp(); AuthenticationService.Instance.SignOut(); AuthenticationService.Instance.ClearSessionToken(); } } /// /// Delete current account /// public static void DeleteAccount() { AuthenticationService.Instance.DeleteAccountAsync(); } /// /// Gets the current user's information. /// /// A containing the current user's data. public static UserInfoModel GetUserInfo() { _USER_INFO_MODEL.player_name = AuthenticationService.Instance.PlayerName; _USER_INFO_MODEL.accountId = AuthenticationService.Instance.PlayerId; return _USER_INFO_MODEL; } /// /// Refresh the authentication token /// /// The old token public static void ProcessAuthenticationTokens(string _token) { AuthenticationService.Instance.ProcessAuthenticationTokens(_token); } /// /// Updates the current player's display name. /// /// The new display name. public static void UpdatePlayerName(string _playerName) { AuthenticationService.Instance.UpdatePlayerNameAsync(_playerName); } /// /// Whether a user is currently signed in. /// public static bool IsSignedIn => AuthenticationService.Instance.IsSignedIn; /// /// Whether the current session is authorized. /// public static bool IsAuthorized => AuthenticationService.Instance.IsAuthorized; /// /// Whether the current session token has expired. /// public static bool IsExpired => AuthenticationService.Instance.IsExpired; /// /// Whether a session token exists on the local device. /// public static bool SessionTokenExists => AuthenticationService.Instance.SessionTokenExists; } }