No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UniWebViewAuthenticationFlowLine.cs 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //
  2. // UniWebViewAuthenticationFlowLine.cs
  3. // Created by Wang Wei (@onevcat) on 2022-06-25.
  4. //
  5. // This file is a part of UniWebView Project (https://uniwebview.com)
  6. // By purchasing the asset, you are allowed to use this code in as many as projects
  7. // you want, only if you publish the final products under the name of the same account
  8. // used for the purchase.
  9. //
  10. // This asset and all corresponding files (such as source code) are provided on an
  11. // “as is” basis, without warranty of any kind, express of implied, including but not
  12. // limited to the warranties of merchantability, fitness for a particular purpose, and
  13. // noninfringement. In no event shall the authors or copyright holders be liable for any
  14. // claim, damages or other liability, whether in action of contract, tort or otherwise,
  15. // arising from, out of or in connection with the software or the use of other dealing in the software.
  16. //
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Collections.Specialized;
  20. using UnityEngine;
  21. using UnityEngine.Events;
  22. /// <summary>
  23. /// A predefined authentication flow LINE Login.
  24. ///
  25. /// This implementation follows the flow described here:
  26. /// https://developers.line.biz/en/reference/line-login/
  27. ///
  28. /// Google authentication flow is a bit different from the other standard authentication flows. Please read the link
  29. /// above carefully to understand it.
  30. ///
  31. /// See https://docs.uniwebview.com/guide/oauth2.html for a more detailed guide of authentication in UniWebView.
  32. /// </summary>
  33. public class UniWebViewAuthenticationFlowLine : UniWebViewAuthenticationCommonFlow, IUniWebViewAuthenticationFlow<UniWebViewAuthenticationLineToken> {
  34. /// <summary>
  35. /// The client ID (Channel ID) of your LINE Login application.
  36. /// </summary>
  37. public string clientId = "";
  38. /// <summary>
  39. /// The iOS bundle Id you set in LINE developer console.
  40. /// </summary>
  41. public string iOSBundleId = "";
  42. /// <summary>
  43. /// The Android package name you set in LINE developer console.
  44. /// </summary>
  45. public string androidPackageName = "";
  46. /// <summary>
  47. /// The scope of your LINE application.
  48. /// </summary>
  49. public string scope = "";
  50. /// <summary>
  51. /// Optional to control this flow's behaviour.
  52. /// </summary>
  53. public UniWebViewAuthenticationFlowLineOptional optional;
  54. private string responseType = "code";
  55. private string grantType = "authorization_code";
  56. private string RedirectUri {
  57. get {
  58. if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXEditor) {
  59. return $"line3rdp.{iOSBundleId}://auth";
  60. }
  61. if (Application.platform == RuntimePlatform.Android) {
  62. return "intent://auth#Intent;package=" + androidPackageName + ";scheme=lineauth;end";
  63. }
  64. UniWebViewLogger.Instance.Critical("Not supported platform for LINE Login.");
  65. return "";
  66. }
  67. }
  68. private readonly UniWebViewAuthenticationConfiguration config =
  69. new UniWebViewAuthenticationConfiguration(
  70. "https://access.line.me/oauth2/v2.1/login",
  71. "https://api.line.me/oauth2/v2.1/token"
  72. );
  73. /// <summary>
  74. /// Starts the authentication flow with the standard OAuth 2.0.
  75. /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`.
  76. /// </summary>
  77. public override void StartAuthenticationFlow() {
  78. var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationLineToken>(this);
  79. flow.StartAuth();
  80. }
  81. /// <summary>
  82. /// Starts the refresh flow with the standard OAuth 2.0.
  83. /// This implements the abstract method in `UniWebViewAuthenticationCommonFlow`.
  84. /// </summary>
  85. /// <param name="refreshToken">The refresh token received with a previous access token response.</param>
  86. public override void StartRefreshTokenFlow(string refreshToken) {
  87. var flow = new UniWebViewAuthenticationFlow<UniWebViewAuthenticationLineToken>(this);
  88. flow.RefreshToken(refreshToken);
  89. }
  90. /// <summary>
  91. /// Implements required method in `IUniWebViewAuthenticationFlow`.
  92. /// </summary>
  93. public UniWebViewAuthenticationConfiguration GetAuthenticationConfiguration() {
  94. return config;
  95. }
  96. /// <summary>
  97. /// Implements required method in `IUniWebViewAuthenticationFlow`.
  98. /// </summary>
  99. public string GetCallbackUrl() {
  100. return RedirectUri;
  101. }
  102. /// <summary>
  103. /// Implements required method in `IUniWebViewAuthenticationFlow`.
  104. /// </summary>
  105. public Dictionary<string, string> GetAuthenticationUriArguments() {
  106. var authorizeArgs = new Dictionary<string, string> {
  107. { "loginChannelId", clientId },
  108. { "returnUri", GenerateReturnUri() },
  109. };
  110. return authorizeArgs;
  111. }
  112. private string GenerateReturnUri() {
  113. var query = new NameValueCollection {
  114. { "response_type", responseType },
  115. { "client_id", clientId },
  116. { "redirect_uri", RedirectUri }
  117. };
  118. // State is a must in LINE Login.
  119. var state = GenerateAndStoreState();
  120. query.Add("state", state);
  121. if (!String.IsNullOrEmpty(scope)) {
  122. query.Add("scope", scope);
  123. } else {
  124. query.Add("scope", "profile");
  125. }
  126. if (optional != null) {
  127. if (optional.PKCESupport != UniWebViewAuthenticationPKCE.None) {
  128. var codeChallenge = GenerateCodeChallengeAndStoreCodeVerify(optional.PKCESupport);
  129. query.Add("code_challenge", codeChallenge);
  130. var method = UniWebViewAuthenticationUtils.ConvertPKCEToString(optional.PKCESupport);
  131. query.Add("code_challenge_method", method);
  132. }
  133. }
  134. return "/oauth2/v2.1/authorize/consent?" + UniWebViewAuthenticationUtils.CreateQueryString(query);
  135. }
  136. /// <summary>
  137. /// Implements required method in `IUniWebViewAuthenticationFlow`.
  138. /// </summary>
  139. public Dictionary<string, string> GetAccessTokenRequestParameters(string authResponse) {
  140. var normalizedRedirectUri = UniWebViewAuthenticationUtils.ConvertIntentUri(RedirectUri);
  141. if (!authResponse.StartsWith(normalizedRedirectUri)) {
  142. throw AuthenticationResponseException.UnexpectedAuthCallbackUrl;
  143. }
  144. var uri = new Uri(authResponse);
  145. var response = UniWebViewAuthenticationUtils.ParseFormUrlEncodedString(uri.Query);
  146. VerifyState(response);
  147. if (!response.TryGetValue("code", out var code)) {
  148. throw AuthenticationResponseException.InvalidResponse(authResponse);
  149. }
  150. var parameters = new Dictionary<string, string> {
  151. { "client_id", clientId },
  152. { "code", code },
  153. { "redirect_uri", RedirectUri },
  154. { "grant_type", grantType },
  155. };
  156. if (CodeVerify != null) {
  157. parameters.Add("code_verifier", CodeVerify);
  158. }
  159. return parameters;
  160. }
  161. /// <summary>
  162. /// Implements required method in `IUniWebViewAuthenticationFlow`.
  163. /// </summary>
  164. public Dictionary<string, string> GetRefreshTokenRequestParameters(string refreshToken) {
  165. return new Dictionary<string, string> {
  166. { "client_id", clientId },
  167. { "refresh_token", refreshToken },
  168. { "grant_type", "refresh_token" }
  169. };
  170. }
  171. /// <summary>
  172. /// Implements required method in `IUniWebViewAuthenticationFlow`.
  173. /// </summary>
  174. public UniWebViewAuthenticationLineToken GenerateTokenFromExchangeResponse(string exchangeResponse) {
  175. return UniWebViewAuthenticationTokenFactory<UniWebViewAuthenticationLineToken>.Parse(exchangeResponse);
  176. }
  177. [field: SerializeField]
  178. public UnityEvent<UniWebViewAuthenticationLineToken> OnAuthenticationFinished { get; set; }
  179. [field: SerializeField]
  180. public UnityEvent<long, string> OnAuthenticationErrored { get; set; }
  181. [field: SerializeField]
  182. public UnityEvent<UniWebViewAuthenticationLineToken> OnRefreshTokenFinished { get; set; }
  183. [field: SerializeField]
  184. public UnityEvent<long, string> OnRefreshTokenErrored { get; set; }
  185. }
  186. /// <summary>
  187. /// The authentication flow's optional settings for LINE.
  188. /// </summary>
  189. [Serializable]
  190. public class UniWebViewAuthenticationFlowLineOptional {
  191. /// <summary>
  192. /// Whether to enable PKCE when performing authentication. Default is `S256`.
  193. /// </summary>
  194. public UniWebViewAuthenticationPKCE PKCESupport = UniWebViewAuthenticationPKCE.S256;
  195. }
  196. /// <summary>
  197. /// The token object from LINE. Check `UniWebViewAuthenticationStandardToken` for more.
  198. /// </summary>
  199. public class UniWebViewAuthenticationLineToken : UniWebViewAuthenticationStandardToken { }