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;
}
}