AuthDelegateImplementation.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using System.Linq;
  3. using System.Security.Cryptography.X509Certificates;
  4. using System.Threading.Tasks;
  5. using Microsoft.Identity.Client;
  6. using Microsoft.InformationProtection;
  7. namespace AipGateway.AIP
  8. {
  9. public class AuthDelegateImplementation : IAuthDelegate
  10. {
  11. public int LastErrNo { get; set; }
  12. public string LastErrMsg { get; set; }
  13. private readonly AipConfig _aipConfig;
  14. public AuthDelegateImplementation(AipConfig aipConfig)
  15. {
  16. _aipConfig = aipConfig;
  17. LastErrNo = 0;
  18. LastErrMsg = String.Empty;
  19. }
  20. public void ResetError()
  21. {
  22. LastErrNo = 0;
  23. LastErrMsg = string.Empty;
  24. }
  25. private void SetError(int errNo, string errMsg1, string errMsg2 = "No Exception Message.")
  26. {
  27. LastErrNo = errNo;
  28. LastErrMsg = errMsg1 + "\r\n" + errMsg2;
  29. Console.WriteLine("AuthDelegateImplementation::SetError ==> {0}, {1}, {2}", errNo, errMsg1, errMsg2);
  30. }
  31. public string AcquireToken(Identity identity, string authority, string resource, string claim)
  32. {
  33. Console.WriteLine("AuthDelegateImplementation::AcquireToken ==> LoginType: {0}", _aipConfig.LoginType);
  34. if (_aipConfig.LoginType == AipAuthLoginType.authLoginPassword)
  35. {
  36. return AcquireTokenByPassword(identity, authority, resource, claim);
  37. }
  38. if (_aipConfig.LoginType == AipAuthLoginType.authLoginCert)
  39. {
  40. return AcquireTokenByCert(identity, authority, resource, claim);
  41. }
  42. return AcquireTokenById(identity, authority, resource, claim);
  43. }
  44. private string AcquireTokenByPassword(Identity identity, string authority, string resource, string claims)
  45. {
  46. Console.WriteLine("AuthDelegateImplementation::AcquireTokenByPassword");
  47. AuthenticationResult result;
  48. var authorityUri = new Uri(authority);
  49. authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
  50. var app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  51. .WithClientSecret(_aipConfig.SecretValue)
  52. .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
  53. .Build();
  54. var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  55. //app.AddInMemoryTokenCache();
  56. try
  57. {
  58. result = app.AcquireTokenForClient(scopes)
  59. .WithTenantId(_aipConfig.TenantId)
  60. .ExecuteAsync()
  61. .GetAwaiter()
  62. .GetResult();
  63. }
  64. catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
  65. {
  66. // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
  67. // Mitigation: change the scope to be as expected
  68. SetError(1, "AcquireTokenByPassword.Scope provided is not supported.", ex.Message);
  69. return null;
  70. }
  71. return result.AccessToken;
  72. }
  73. private string AcquireTokenByCert(Identity identity, string authority, string resource, string claims)
  74. {
  75. Console.WriteLine("AuthDelegateImplementation::AcquireTokenByPassword");
  76. AuthenticationResult result;
  77. var authorityUri = new Uri(authority);
  78. authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
  79. // Read cert from local machine
  80. //var certificate = ReadCertificateFromStore(certThumb);
  81. var myCertificate = X509Certificate2.CreateFromCertFile(_aipConfig.CertThumbPrint);
  82. X509Certificate2 certificate = new X509Certificate2(myCertificate);
  83. // Use cert to build ClientAssertionCertificate
  84. var app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  85. .WithCertificate(certificate)
  86. .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
  87. .Build();
  88. // Append .default to the resource passed in to AcquireToken().
  89. var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  90. try
  91. {
  92. result = app.AcquireTokenForClient(scopes).ExecuteAsync().Result;
  93. }
  94. catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
  95. {
  96. // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
  97. // Mitigation: change the scope to be as expected
  98. SetError(1, "AcquireTokenByCert.Scope provided is not supported.", ex.Message);
  99. return null;
  100. }
  101. return result.AccessToken;
  102. }
  103. private string AcquireTokenById(Identity identity, string authority, string resource, string claims)
  104. {
  105. Console.WriteLine("AcquireTokenById::AcquireTokenByPassword");
  106. AuthenticationResult result;
  107. var authorityUri = new Uri(authority);
  108. authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
  109. var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
  110. .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
  111. .WithAuthority(AzureCloudInstance.AzurePublic, "2e58414a-c6ae-43ff-aaf5-45ab8b78a404")
  112. .Build();
  113. var accounts = (app.GetAccountsAsync()).GetAwaiter().GetResult();
  114. // Append .default to the resource passed in to AcquireToken().
  115. var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  116. try
  117. {
  118. result = app.AcquireTokenInteractive(scopes)
  119. .WithAccount(accounts.FirstOrDefault())
  120. .WithPrompt(Prompt.SelectAccount)
  121. .ExecuteAsync()
  122. .ConfigureAwait(false)
  123. .GetAwaiter()
  124. .GetResult();
  125. }
  126. catch (Exception ex)
  127. {
  128. SetError(1, "AcquireTokenById.Login Failed.", ex.Message);
  129. return null;
  130. }
  131. return result.AccessToken;
  132. }
  133. private static X509Certificate2 ReadCertificateFromStore(string thumbprint)
  134. {
  135. X509Certificate2 cert = null;
  136. X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
  137. store.Open(OpenFlags.ReadOnly);
  138. X509Certificate2Collection certCollection = store.Certificates;
  139. // Find unexpired certificates.
  140. X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
  141. // From the collection of unexpired certificates, find the ones with the correct name.
  142. X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindByThumbprint, thumbprint, false);
  143. // Return the first certificate in the collection, has the right name and is current.
  144. cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
  145. store.Close();
  146. return cert;
  147. }
  148. public async Task<AuthenticationResult> AcquireTokenAsync(string authority, string resource, string claims)
  149. {
  150. AuthenticationResult result = null;
  151. if (authority.ToLower().Contains("common"))
  152. {
  153. var authorityUri = new Uri(authority);
  154. authority = String.Format("https://{0}/{1}", authorityUri.Host, _aipConfig.TenantId);
  155. }
  156. var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
  157. .WithAuthority(authority)
  158. .WithDefaultRedirectUri()
  159. .Build();
  160. var accounts = (app.GetAccountsAsync()).GetAwaiter().GetResult();
  161. // Append .default to the resource passed in to AcquireToken().
  162. string[] scopes = { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  163. try
  164. {
  165. result = await app.AcquireTokenSilent(new[] { "https://aadrm.com/user_impersonation" }, accounts.FirstOrDefault()).ExecuteAsync();
  166. // result = await _app.AcquireTokenSilent(scopes,
  167. // accounts.FirstOrDefault())
  168. // .ExecuteAsync();
  169. }
  170. catch (MsalUiRequiredException)
  171. {
  172. result = app.AcquireTokenInteractive(scopes)
  173. .WithAccount(accounts.FirstOrDefault())
  174. .WithPrompt(Prompt.SelectAccount)
  175. .ExecuteAsync()
  176. .ConfigureAwait(false)
  177. .GetAwaiter()
  178. .GetResult();
  179. }
  180. // Return the token. The token is sent to the resource.
  181. return result;
  182. }
  183. // private async Task<string> GetAccessTokenOnBehalfOfUser(string authority, string resource)
  184. // {
  185. // IConfidentialClientApplication app;
  186. //
  187. // if (false)
  188. // {
  189. // // Read X509 cert from local store and build ClientAssertionCertificate.
  190. // X509Certificate2 cert = Utilities.ReadCertificateFromStore(_aipConfig.CertThumbPrint);
  191. //
  192. // // Create confidential client using certificate.
  193. // app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  194. // .WithRedirectUri(resource)
  195. // .WithAuthority(authority)
  196. // .WithCertificate(cert)
  197. // .Build();
  198. // }
  199. //
  200. // else
  201. // {
  202. // // Create confidential client using client secret.
  203. // app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  204. // .WithRedirectUri(resource)
  205. // .WithAuthority(authority)
  206. // .WithClientSecret(_aipConfig.SecretValue)
  207. // .Build();
  208. // }
  209. //
  210. // // Store user access token of authenticated user.
  211. // var ci = (ClaimsIdentity)_claimsPrincipal.Identity;
  212. // string userAccessToken = (string)ci.BootstrapContext;
  213. //
  214. //
  215. // // Generate a user assertion with the UPN and access token.
  216. // UserAssertion userAssertion = new UserAssertion(userAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer");
  217. //
  218. // // Append .default to the resource passed in to AcquireToken().
  219. // List<string> scopes = new List<string>() { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  220. //
  221. // AuthenticationResult result = await app.AcquireTokenOnBehalfOf(scopes, userAssertion)
  222. // .ExecuteAsync();
  223. //
  224. // // Return the token to the API caller
  225. // return (result.AccessToken);
  226. // }
  227. }
  228. }