AppleAuthManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #if ((UNITY_IOS || UNITY_TVOS || UNITY_STANDALONE_OSX) && !UNITY_EDITOR)
  2. #define APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  3. #endif
  4. using AppleAuth.Enums;
  5. using AppleAuth.Interfaces;
  6. using System;
  7. namespace AppleAuth
  8. {
  9. public class AppleAuthManager : IAppleAuthManager
  10. {
  11. static AppleAuthManager()
  12. {
  13. const string versionMessage = "Using Sign in with Apple Unity Plugin - v1.4.2";
  14. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  15. PInvoke.AppleAuth_LogMessage(versionMessage);
  16. #else
  17. UnityEngine.Debug.Log(versionMessage);
  18. #endif
  19. }
  20. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  21. private readonly IPayloadDeserializer _payloadDeserializer;
  22. private Action<string> _credentialsRevokedCallback;
  23. #endif
  24. public static bool IsCurrentPlatformSupported
  25. {
  26. get
  27. {
  28. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  29. return PInvoke.AppleAuth_IsCurrentPlatformSupported();
  30. #else
  31. return false;
  32. #endif
  33. }
  34. }
  35. public AppleAuthManager(IPayloadDeserializer payloadDeserializer)
  36. {
  37. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  38. this._payloadDeserializer = payloadDeserializer;
  39. #endif
  40. }
  41. public void QuickLogin(Action<ICredential> successCallback, Action<IAppleError> errorCallback)
  42. {
  43. this.QuickLogin(new AppleAuthQuickLoginArgs(), successCallback, errorCallback);
  44. }
  45. public void QuickLogin(
  46. AppleAuthQuickLoginArgs quickLoginArgs,
  47. Action<ICredential> successCallback,
  48. Action<IAppleError> errorCallback)
  49. {
  50. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  51. var nonce = quickLoginArgs.Nonce;
  52. var state = quickLoginArgs.State;
  53. var requestId = CallbackHandler.AddMessageCallback(
  54. true,
  55. payload =>
  56. {
  57. var response = this._payloadDeserializer.DeserializeLoginWithAppleIdResponse(payload);
  58. if (response.Error != null)
  59. errorCallback(response.Error);
  60. else if (response.PasswordCredential != null)
  61. successCallback(response.PasswordCredential);
  62. else
  63. successCallback(response.AppleIDCredential);
  64. });
  65. PInvoke.AppleAuth_QuickLogin(requestId, nonce, state);
  66. #else
  67. throw new Exception("AppleAuthManager is not supported in this platform");
  68. #endif
  69. }
  70. public void LoginWithAppleId(LoginOptions options, Action<ICredential> successCallback, Action<IAppleError> errorCallback)
  71. {
  72. this.LoginWithAppleId(new AppleAuthLoginArgs(options), successCallback, errorCallback);
  73. }
  74. public void LoginWithAppleId(
  75. AppleAuthLoginArgs loginArgs,
  76. Action<ICredential> successCallback,
  77. Action<IAppleError> errorCallback)
  78. {
  79. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  80. var loginOptions = loginArgs.Options;
  81. var nonce = loginArgs.Nonce;
  82. var state = loginArgs.State;
  83. var requestId = CallbackHandler.AddMessageCallback(
  84. true,
  85. payload =>
  86. {
  87. var response = this._payloadDeserializer.DeserializeLoginWithAppleIdResponse(payload);
  88. if (response.Error != null)
  89. errorCallback(response.Error);
  90. else
  91. successCallback(response.AppleIDCredential);
  92. });
  93. PInvoke.AppleAuth_LoginWithAppleId(requestId, (int)loginOptions, nonce, state);
  94. #else
  95. throw new Exception("AppleAuthManager is not supported in this platform");
  96. #endif
  97. }
  98. public void GetCredentialState(
  99. string userId,
  100. Action<CredentialState> successCallback,
  101. Action<IAppleError> errorCallback)
  102. {
  103. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  104. var requestId = CallbackHandler.AddMessageCallback(
  105. true,
  106. payload =>
  107. {
  108. var response = this._payloadDeserializer.DeserializeCredentialStateResponse(payload);
  109. if (response.Error != null)
  110. errorCallback(response.Error);
  111. else
  112. successCallback(response.CredentialState);
  113. });
  114. PInvoke.AppleAuth_GetCredentialState(requestId, userId);
  115. #else
  116. throw new Exception("AppleAuthManager is not supported in this platform");
  117. #endif
  118. }
  119. public void SetCredentialsRevokedCallback(Action<string> credentialsRevokedCallback)
  120. {
  121. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  122. if (this._credentialsRevokedCallback != null)
  123. {
  124. CallbackHandler.NativeCredentialsRevoked -= this._credentialsRevokedCallback;
  125. this._credentialsRevokedCallback = null;
  126. }
  127. if (credentialsRevokedCallback != null)
  128. {
  129. CallbackHandler.NativeCredentialsRevoked += credentialsRevokedCallback;
  130. this._credentialsRevokedCallback = credentialsRevokedCallback;
  131. }
  132. #endif
  133. }
  134. public void Update()
  135. {
  136. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  137. CallbackHandler.ExecutePendingCallbacks();
  138. #endif
  139. }
  140. #if APPLE_AUTH_MANAGER_NATIVE_IMPLEMENTATION_AVAILABLE
  141. private static class CallbackHandler
  142. {
  143. private const uint InitialCallbackId = 1U;
  144. private const uint MaxCallbackId = uint.MaxValue;
  145. private static readonly object SyncLock = new object();
  146. private static readonly System.Collections.Generic.Dictionary<uint, Entry> CallbackDictionary = new System.Collections.Generic.Dictionary<uint, Entry>();
  147. private static readonly System.Collections.Generic.List<Action> ScheduledActions = new System.Collections.Generic.List<Action>();
  148. private static uint _callbackId = InitialCallbackId;
  149. private static bool _initialized = false;
  150. private static uint _credentialsRevokedCallbackId = 0U;
  151. private static event Action<string> _nativeCredentialsRevoked = null;
  152. public static event Action<string> NativeCredentialsRevoked
  153. {
  154. add
  155. {
  156. lock (SyncLock)
  157. {
  158. if (_nativeCredentialsRevoked == null)
  159. {
  160. _credentialsRevokedCallbackId = AddMessageCallback(false, payload => _nativeCredentialsRevoked.Invoke(payload));
  161. PInvoke.AppleAuth_RegisterCredentialsRevokedCallbackId(_credentialsRevokedCallbackId);
  162. }
  163. _nativeCredentialsRevoked += value;
  164. }
  165. }
  166. remove
  167. {
  168. lock (SyncLock)
  169. {
  170. _nativeCredentialsRevoked -= value;
  171. if (_nativeCredentialsRevoked == null)
  172. {
  173. RemoveMessageCallback(_credentialsRevokedCallbackId);
  174. _credentialsRevokedCallbackId = 0U;
  175. PInvoke.AppleAuth_RegisterCredentialsRevokedCallbackId(0U);
  176. }
  177. }
  178. }
  179. }
  180. public static void ScheduleCallback(uint requestId, string payload)
  181. {
  182. lock (SyncLock)
  183. {
  184. var callbackEntry = default(Entry);
  185. if (CallbackDictionary.TryGetValue(requestId, out callbackEntry))
  186. {
  187. var callback = callbackEntry.MessageCallback;
  188. ScheduledActions.Add(() => callback.Invoke(payload));
  189. if (callbackEntry.IsSingleUseCallback)
  190. {
  191. CallbackDictionary.Remove(requestId);
  192. }
  193. }
  194. }
  195. }
  196. public static void ExecutePendingCallbacks()
  197. {
  198. lock (SyncLock)
  199. {
  200. while (ScheduledActions.Count > 0)
  201. {
  202. var action = ScheduledActions[0];
  203. ScheduledActions.RemoveAt(0);
  204. action.Invoke();
  205. }
  206. }
  207. }
  208. public static uint AddMessageCallback(bool isSingleUse, Action<string> messageCallback)
  209. {
  210. if (!_initialized)
  211. {
  212. PInvoke.AppleAuth_SetupNativeMessageHandlerCallback(PInvoke.NativeMessageHandlerCallback);
  213. _initialized = true;
  214. }
  215. if (messageCallback == null)
  216. {
  217. throw new Exception("Can't add a null Message Callback.");
  218. }
  219. var usedCallbackId = default(uint);
  220. lock (SyncLock)
  221. {
  222. usedCallbackId = _callbackId;
  223. _callbackId += 1;
  224. if (_callbackId >= MaxCallbackId)
  225. _callbackId = InitialCallbackId;
  226. var callbackEntry = new Entry(isSingleUse, messageCallback);
  227. CallbackDictionary.Add(usedCallbackId, callbackEntry);
  228. }
  229. return usedCallbackId;
  230. }
  231. public static void RemoveMessageCallback(uint requestId)
  232. {
  233. lock (SyncLock)
  234. {
  235. if (!CallbackDictionary.ContainsKey(requestId))
  236. {
  237. throw new Exception("Callback with id " + requestId + " does not exist and can't be removed");
  238. }
  239. CallbackDictionary.Remove(requestId);
  240. }
  241. }
  242. private class Entry
  243. {
  244. public readonly bool IsSingleUseCallback;
  245. public readonly Action<string> MessageCallback;
  246. public Entry(bool isSingleUseCallback, Action<string> messageCallback)
  247. {
  248. this.IsSingleUseCallback = isSingleUseCallback;
  249. this.MessageCallback = messageCallback;
  250. }
  251. }
  252. }
  253. private static class PInvoke
  254. {
  255. #if UNITY_IOS || UNITY_TVOS
  256. private const string DllName = "__Internal";
  257. #elif UNITY_STANDALONE_OSX
  258. private const string DllName = "MacOSAppleAuthManager";
  259. #endif
  260. public delegate void NativeMessageHandlerCallbackDelegate(uint requestId, string payload);
  261. [AOT.MonoPInvokeCallback(typeof(NativeMessageHandlerCallbackDelegate))]
  262. public static void NativeMessageHandlerCallback(uint requestId, string payload)
  263. {
  264. try
  265. {
  266. CallbackHandler.ScheduleCallback(requestId, payload);
  267. }
  268. catch (Exception exception)
  269. {
  270. Console.WriteLine("Received exception while scheduling a callback for request ID " + requestId);
  271. Console.WriteLine("Detailed payload:\n" + payload);
  272. Console.WriteLine("Exception: " + exception);
  273. }
  274. }
  275. [System.Runtime.InteropServices.DllImport(DllName)]
  276. public static extern bool AppleAuth_IsCurrentPlatformSupported();
  277. [System.Runtime.InteropServices.DllImport(DllName)]
  278. public static extern void AppleAuth_SetupNativeMessageHandlerCallback(NativeMessageHandlerCallbackDelegate callback);
  279. [System.Runtime.InteropServices.DllImport(DllName)]
  280. public static extern void AppleAuth_GetCredentialState(uint requestId, string userId);
  281. [System.Runtime.InteropServices.DllImport(DllName)]
  282. public static extern void AppleAuth_LoginWithAppleId(uint requestId, int loginOptions, string nonceCStr, string stateCStr);
  283. [System.Runtime.InteropServices.DllImport(DllName)]
  284. public static extern void AppleAuth_QuickLogin(uint requestId, string nonceCStr, string stateCStr);
  285. [System.Runtime.InteropServices.DllImport(DllName)]
  286. public static extern void AppleAuth_RegisterCredentialsRevokedCallbackId(uint callbackId);
  287. [System.Runtime.InteropServices.DllImport(DllName)]
  288. public static extern void AppleAuth_LogMessage(string messageCStr);
  289. }
  290. #endif
  291. }
  292. }