AndroidTokenClient.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // <copyright file="AndroidTokenClient.cs" company="Google Inc.">
  2. // Copyright (C) 2015 Google Inc.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. // </copyright>
  16. #if UNITY_ANDROID
  17. namespace GooglePlayGames.Android
  18. {
  19. using System;
  20. using System.Linq;
  21. using BasicApi;
  22. using OurUtils;
  23. using UnityEngine;
  24. using System.Collections.Generic;
  25. internal class AndroidTokenClient : TokenClient
  26. {
  27. private const string HelperFragmentClass = "com.google.games.bridge.HelperFragment";
  28. // These are the configuration values.
  29. private bool requestEmail;
  30. private bool requestAuthCode;
  31. private bool requestIdToken;
  32. private List<string> oauthScopes;
  33. private string webClientId;
  34. private bool forceRefresh;
  35. private bool hidePopups;
  36. private string accountName;
  37. // These are the results
  38. private AndroidJavaObject account;
  39. private string email;
  40. private string authCode;
  41. private string idToken;
  42. public void SetRequestAuthCode(bool flag, bool forceRefresh)
  43. {
  44. requestAuthCode = flag;
  45. this.forceRefresh = forceRefresh;
  46. }
  47. public void SetRequestEmail(bool flag)
  48. {
  49. requestEmail = flag;
  50. }
  51. public void SetRequestIdToken(bool flag)
  52. {
  53. requestIdToken = flag;
  54. }
  55. public void SetWebClientId(string webClientId)
  56. {
  57. this.webClientId = webClientId;
  58. }
  59. public void SetHidePopups(bool flag)
  60. {
  61. this.hidePopups = flag;
  62. }
  63. public void SetAccountName(string accountName)
  64. {
  65. this.accountName = accountName;
  66. }
  67. public void AddOauthScopes(params string[] scopes)
  68. {
  69. if (scopes != null)
  70. {
  71. if (oauthScopes == null)
  72. {
  73. oauthScopes = new List<string>();
  74. }
  75. oauthScopes.AddRange(scopes);
  76. }
  77. }
  78. public void Signout()
  79. {
  80. account = null;
  81. authCode = null;
  82. email = null;
  83. idToken = null;
  84. PlayGamesHelperObject.RunOnGameThread(() =>
  85. {
  86. Debug.Log("Calling Signout in token client");
  87. AndroidJavaClass cls = new AndroidJavaClass(HelperFragmentClass);
  88. cls.CallStatic("signOut", AndroidHelperFragment.GetActivity());
  89. });
  90. }
  91. /// <summary>Gets the email selected by the current player.</summary>
  92. /// <remarks>This is not necessarily the email address of the player. It
  93. /// is just the account selected by the player from a list of accounts
  94. /// present on the device.
  95. /// </remarks>
  96. /// <returns>A string representing the email.</returns>
  97. public string GetEmail()
  98. {
  99. return email;
  100. }
  101. public string GetAuthCode()
  102. {
  103. return authCode;
  104. }
  105. /// <summary>Gets the OpenID Connect ID token for authentication with a server backend.</summary>
  106. /// <param name="serverClientId">Server client ID from console.developers.google.com or the Play Games
  107. /// services console.</param>
  108. /// <param name="idTokenCallback"> A callback to be invoked after token is retrieved. Will be passed null value
  109. /// on failure. </param>
  110. public string GetIdToken()
  111. {
  112. return idToken;
  113. }
  114. public void FetchTokens(bool silent, Action<int> callback)
  115. {
  116. PlayGamesHelperObject.RunOnGameThread(() => DoFetchToken(silent, callback));
  117. }
  118. public void RequestPermissions(string[] scopes, Action<SignInStatus> callback)
  119. {
  120. using (var bridgeClass = new AndroidJavaClass(HelperFragmentClass))
  121. using (var currentActivity = AndroidHelperFragment.GetActivity())
  122. using (var task =
  123. bridgeClass.CallStatic<AndroidJavaObject>("showRequestPermissionsUi", currentActivity,
  124. oauthScopes.Union(scopes).ToArray()))
  125. {
  126. AndroidTaskUtils.AddOnSuccessListener<AndroidJavaObject>(task, /* disposeResult= */ false,
  127. accountWithNewScopes =>
  128. {
  129. if (accountWithNewScopes == null)
  130. {
  131. callback(SignInStatus.InternalError);
  132. return;
  133. }
  134. account = accountWithNewScopes;
  135. email = account.Call<string>("getEmail");
  136. idToken = account.Call<string>("getIdToken");
  137. authCode = account.Call<string>("getServerAuthCode");
  138. oauthScopes = oauthScopes.Union(scopes).ToList();
  139. callback(SignInStatus.Success);
  140. });
  141. AndroidTaskUtils.AddOnFailureListener(task, e =>
  142. {
  143. if (!Misc.IsApiException(e)) {
  144. OurUtils.Logger.e("Exception requesting new permissions" +
  145. e.Call<string>("toString"));
  146. return;
  147. }
  148. var failCode = SignInHelper.ToSignInStatus(e.Call<int>("getStatusCode"));
  149. OurUtils.Logger.e("Exception requesting new permissions: " + failCode);
  150. callback(failCode);
  151. });
  152. }
  153. }
  154. /// <summary>Returns whether or not user has given permissions for given scopes.</summary>
  155. /// <param name="scopes">array of scopes</param>
  156. /// <returns><c>true</c>, if given, <c>false</c> otherwise.</returns>
  157. public bool HasPermissions(string[] scopes)
  158. {
  159. using (var bridgeClass = new AndroidJavaClass(HelperFragmentClass))
  160. using (var currentActivity = AndroidHelperFragment.GetActivity())
  161. {
  162. return bridgeClass.CallStatic<bool>("hasPermissions", currentActivity, scopes);
  163. }
  164. }
  165. private void DoFetchToken(bool silent, Action<int> callback)
  166. {
  167. try
  168. {
  169. using (var bridgeClass = new AndroidJavaClass(HelperFragmentClass))
  170. using (var currentActivity = AndroidHelperFragment.GetActivity())
  171. using (var pendingResult = bridgeClass.CallStatic<AndroidJavaObject>(
  172. "fetchToken",
  173. currentActivity,
  174. silent,
  175. requestAuthCode,
  176. requestEmail,
  177. requestIdToken,
  178. webClientId,
  179. forceRefresh,
  180. oauthScopes.ToArray(),
  181. hidePopups,
  182. accountName))
  183. {
  184. pendingResult.Call("setResultCallback", new ResultCallbackProxy(
  185. tokenResult =>
  186. {
  187. account = tokenResult.Call<AndroidJavaObject>("getAccount");
  188. authCode = tokenResult.Call<string>("getAuthCode");
  189. email = tokenResult.Call<string>("getEmail");
  190. idToken = tokenResult.Call<string>("getIdToken");
  191. callback(tokenResult.Call<int>("getStatusCode"));
  192. }));
  193. }
  194. }
  195. catch (Exception e)
  196. {
  197. OurUtils.Logger.e("Exception launching token request: " + e.Message);
  198. OurUtils.Logger.e(e.ToString());
  199. }
  200. }
  201. public AndroidJavaObject GetAccount()
  202. {
  203. return account;
  204. }
  205. /// <summary>
  206. /// Gets another server auth code.
  207. /// </summary>
  208. /// <remarks>This method should be called after authenticating, and exchanging
  209. /// the initial server auth code for a token. This is implemented by signing in
  210. /// silently, which if successful returns almost immediately and with a new
  211. /// server auth code.
  212. /// </remarks>
  213. /// <param name="reAuthenticateIfNeeded">Calls Authenticate if needed when
  214. /// retrieving another auth code. </param>
  215. /// <param name="callback">Callback.</param>
  216. public void GetAnotherServerAuthCode(bool reAuthenticateIfNeeded, Action<string> callback)
  217. {
  218. PlayGamesHelperObject.RunOnGameThread(() => DoGetAnotherServerAuthCode(reAuthenticateIfNeeded, callback));
  219. }
  220. private void DoGetAnotherServerAuthCode(bool reAuthenticateIfNeeded, Action<string> callback)
  221. {
  222. try
  223. {
  224. using (var bridgeClass = new AndroidJavaClass(HelperFragmentClass))
  225. using (var currentActivity = AndroidHelperFragment.GetActivity())
  226. using (var pendingResult = bridgeClass.CallStatic<AndroidJavaObject>(
  227. "fetchToken",
  228. currentActivity,
  229. /* silent= */ reAuthenticateIfNeeded,
  230. /* requestAuthCode= */ true,
  231. /* requestEmail= */ false,
  232. /* requestIdToken= */ false,
  233. webClientId,
  234. /* forceRefresh= */ false,
  235. oauthScopes.ToArray(),
  236. /* hidePopups= */ true,
  237. /* accountName= */ ""))
  238. {
  239. pendingResult.Call("setResultCallback", new ResultCallbackProxy(
  240. tokenResult => { callback(tokenResult.Call<string>("getAuthCode")); }));
  241. }
  242. }
  243. catch (Exception e)
  244. {
  245. OurUtils.Logger.e("Exception launching token request: " + e.Message);
  246. OurUtils.Logger.e(e.ToString());
  247. }
  248. }
  249. private class ResultCallbackProxy : AndroidJavaProxy
  250. {
  251. private Action<AndroidJavaObject> mCallback;
  252. public ResultCallbackProxy(Action<AndroidJavaObject> callback)
  253. : base("com/google/android/gms/common/api/ResultCallback")
  254. {
  255. mCallback = callback;
  256. }
  257. public void onResult(AndroidJavaObject tokenResult)
  258. {
  259. mCallback(tokenResult);
  260. }
  261. }
  262. }
  263. }
  264. #endif