AuthDelegateImplementation.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using System;
  2. using System.IdentityModel.Tokens;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Security.Cryptography.X509Certificates;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Microsoft.Identity.Client;
  10. using Microsoft.Identity.Client.Extensions.Msal;
  11. using Microsoft.InformationProtection;
  12. namespace AipGateway.AIP
  13. {
  14. public class AuthDelegateImplementation : IAuthDelegate
  15. {
  16. public int LastErrNo { get; set; }
  17. public string LastErrMsg { get; set; }
  18. private readonly AipConfig _aipConfig;
  19. private readonly IConfidentialClientApplication _confidentialApp = null;
  20. private readonly IPublicClientApplication _publicApp = null;
  21. // [Obsolete("Obsolete")] private TokenCache _tokenCache = new TokenCache();
  22. public AuthDelegateImplementation(AipConfig aipConfig)
  23. {
  24. _aipConfig = aipConfig;
  25. LastErrNo = 0;
  26. LastErrMsg = String.Empty;
  27. var storageProperties =
  28. new StorageCreationPropertiesBuilder(
  29. "AIPGateway.Cache",
  30. Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
  31. .Build();
  32. var cacheHelper = MsalCacheHelper.CreateAsync(storageProperties).GetAwaiter().GetResult();
  33. if (_aipConfig.LoginType == AipAuthLoginType.authLoginPassword)
  34. {
  35. // _confidentialApp = ConfidentialClientApplicationBuilder
  36. // .Create(_aipConfig.ClientId)
  37. // .WithClientSecret(_aipConfig.SecretValue)
  38. // // .WithAuthority($"https://login.microsoftonline.com/{_aipConfig.TenantId}")
  39. // // .WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
  40. // .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
  41. // .Build();
  42. // var authority = $"https://login.windows.net/{_aipConfig.TenantId}";
  43. // BrokerOptions brokerOptions = new BrokerOptions(BrokerOptions.OperatingSystems.Windows);
  44. // _confidentialApp = PublicClientApplicationBuilder
  45. // .Create(_aipConfig.ClientId)
  46. // .WithAuthority(authority)
  47. // .WithDefaultRedirectUri()
  48. // .WithBroker(brokerOptions)
  49. // .Build();
  50. ConfidentialClientApplicationOptions options = new ConfidentialClientApplicationOptions()
  51. {
  52. ClientSecret = _aipConfig.SecretValue,
  53. ClientId = _aipConfig.ClientId,
  54. TenantId = _aipConfig.TenantId,
  55. RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient",
  56. Instance = "https://login.microsoftonline.com/"
  57. };
  58. _confidentialApp = ConfidentialClientApplicationBuilder
  59. .CreateWithApplicationOptions(options)
  60. .WithRedirectUri(options.RedirectUri)
  61. // .WithLegacyCacheCompatibility(false)
  62. // .WithExperimentalFeatures() // for PoP
  63. // .WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
  64. .Build();
  65. // IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(options)
  66. // .WithCertificate(certificate)
  67. // .Build();
  68. //Console.WriteLine(" AppTokenCache: {0}", _confidentialApp.AppTokenCache);
  69. //Console.WriteLine("UserTokenCache: {0}", _confidentialApp.UserTokenCache);
  70. cacheHelper.RegisterCache(_confidentialApp.UserTokenCache);
  71. }
  72. else if (_aipConfig.LoginType == AipAuthLoginType.authLoginCert)
  73. {
  74. var authority = $"https://login.windows.net/{_aipConfig.TenantId}";
  75. // var certificate = Utilities.ReadCertificateFromStore(_aipConfig.CertThumbPrint);
  76. // var myCertificate = X509Certificate2.CreateFromCertFile(_aipConfig.CertThumbPrint, "audwls");
  77. var myCertificate = new X509Certificate2(_aipConfig.CertThumbPrint, "hanteinfo1234!");
  78. X509Certificate2 certificate = new X509Certificate2(myCertificate);
  79. _confidentialApp = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  80. .WithCertificate(certificate)
  81. .WithAuthority(new Uri(authority))
  82. .Build();
  83. cacheHelper.RegisterCache(_confidentialApp.UserTokenCache);
  84. }
  85. else
  86. {
  87. _publicApp = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
  88. .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
  89. .WithAuthority(AzureCloudInstance.AzurePublic, _aipConfig.TenantId)
  90. .Build();
  91. cacheHelper.RegisterCache(_publicApp.UserTokenCache);
  92. }
  93. }
  94. public void ResetError()
  95. {
  96. LastErrNo = 0;
  97. LastErrMsg = string.Empty;
  98. }
  99. private void SetError(int errNo, string errMsg1, string errMsg2 = "No Exception Message.")
  100. {
  101. LastErrNo = errNo;
  102. LastErrMsg = errMsg1 + "\r\n" + errMsg2;
  103. Console.WriteLine("AuthDelegateImplementation::SetError ==> {0}, {1}, {2}", errNo, errMsg1, errMsg2);
  104. }
  105. public string AcquireToken(Identity identity, string authority, string resource, string claim)
  106. {
  107. //Console.WriteLine("AuthDelegateImplementation::AcquireToken ==> LoginType: {0}", _aipConfig.LoginType);
  108. if (_aipConfig.LoginType == AipAuthLoginType.authLoginPassword)
  109. {
  110. return AcquireTokenByCertificate(identity, authority, resource, claim);
  111. }
  112. if (_aipConfig.LoginType == AipAuthLoginType.authLoginCert)
  113. {
  114. return AcquireTokenByCertificate(identity, authority, resource, claim);
  115. }
  116. return AcquireTokenById(identity, authority, resource, claim);
  117. }
  118. private string AcquireTokenByCertificate(Identity identity, string authority, string resource, string claims)
  119. {
  120. // AuthenticationResult result;
  121. // var authorityUri = new Uri(authority);
  122. // authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
  123. var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  124. try
  125. {
  126. //Console.WriteLine("B: AppTokenCache: {0}", _confidentialApp.AppTokenCache);
  127. //Console.WriteLine("B: UserTokenCache: {0}", _confidentialApp.UserTokenCache);
  128. AuthenticationResult result = _confidentialApp.AcquireTokenForClient(scopes)
  129. .ExecuteAsync(CancellationToken.None)
  130. .ConfigureAwait(false)
  131. .GetAwaiter()
  132. .GetResult();
  133. // result = _confidentialApp.AcquireTokenForClient(scopes)
  134. // .WithTenantId(_aipConfig.TenantId)
  135. // .ExecuteAsync()
  136. // .GetAwaiter()
  137. // .GetResult();
  138. // result = _confidentialApp.AcquireTokenForClient(scopes).ExecuteAsync().GetAwaiter().GetResult();
  139. //Console.WriteLine("A: AppTokenCache: {0}", _confidentialApp.AppTokenCache);
  140. //Console.WriteLine("A: UserTokenCache: {0}", _confidentialApp.UserTokenCache);
  141. //Console.WriteLine("A: AccessToken: {0}", result.AccessToken);
  142. return result.AccessToken;
  143. }
  144. catch (MsalUiRequiredException ex) when (ex.Message.Contains("AADSTS70011"))
  145. {
  146. // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
  147. // Mitigation: change the scope to be as expected
  148. SetError(1, "AcquireTokenByCertificate::AcquireTokenByCertificate, Scope provided is not supported.", ex.Message);
  149. }
  150. catch (Exception ex)
  151. {
  152. SetError(1, "AcquireTokenByCertificate::AcquireTokenByCertificate Failed.", ex.Message);
  153. }
  154. return null;
  155. }
  156. // private string AcquireTokenByCertificate_Temp(Identity identity, string authority, string resource, string claims)
  157. // {
  158. // AuthenticationResult result;
  159. // var authorityUri = new Uri(authority);
  160. // authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
  161. //
  162. // var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  163. //
  164. // try
  165. // {
  166. // var accounts = (_confidentialApp.GetAccountsAsync()).GetAwaiter().GetResult();
  167. // result = _confidentialApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
  168. // .ExecuteAsync().GetAwaiter().GetResult();
  169. //
  170. // // result = _confidentialApp.AcquireTokenForClient(scopes)
  171. // // .ExecuteAsync().GetAwaiter().GetResult();
  172. //
  173. // Console.WriteLine("AuthDelegateImplementation::AcquireTokenByCertificate ==> AcquireTokenSilent Succeed.");
  174. // return result.AccessToken;
  175. // }
  176. // catch (MsalUiRequiredException)
  177. // {
  178. // Console.WriteLine("AuthDelegateImplementation::AcquireTokenByCertificate ==> AcquireTokenSilent Failed. Login AcquireTokenForClient.");
  179. // try
  180. // {
  181. // result = _confidentialApp.AcquireTokenForClient(scopes)
  182. // .ExecuteAsync().GetAwaiter().GetResult();
  183. // // result = _confidentialApp.AcquireTokenForClient(scopes)
  184. // // .WithTenantId(_aipConfig.TenantId)
  185. // // .ExecuteAsync()
  186. // // .GetAwaiter()
  187. // // .GetResult();
  188. // // result = _confidentialApp.AcquireTokenForClient(scopes).ExecuteAsync().GetAwaiter().GetResult();
  189. // }
  190. // catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
  191. // {
  192. // // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
  193. // // Mitigation: change the scope to be as expected
  194. // SetError(1, "AcquireTokenByPassword.Scope provided is not supported.", ex.Message);
  195. // return null;
  196. // }
  197. // }
  198. //
  199. // return result.AccessToken;
  200. // }
  201. private string AcquireTokenById(Identity identity, string authority, string resource, string claims)
  202. {
  203. //Console.WriteLine("AcquireTokenById::AcquireTokenByPassword");
  204. AuthenticationResult result;
  205. var authorityUri = new Uri(authority);
  206. authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
  207. // var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
  208. // .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
  209. // .WithAuthority(AzureCloudInstance.AzurePublic, _aipConfig.TenantId)
  210. // .Build();
  211. var accounts = (_publicApp.GetAccountsAsync()).GetAwaiter().GetResult();
  212. // Append .default to the resource passed in to AcquireToken().
  213. var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  214. try
  215. {
  216. result = _publicApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync().Result;
  217. //Console.WriteLine("AuthDelegateImplementation::AcquireTokenById ==> AcquireTokenSilent Succeed.");
  218. return result.AccessToken;
  219. }
  220. catch (MsalUiRequiredException)
  221. {
  222. //Console.WriteLine("AuthDelegateImplementation::AcquireTokenById ==> AcquireTokenSilent Failed. Login AcquireTokenForClient.");
  223. result = _publicApp.AcquireTokenInteractive(scopes)
  224. .WithAccount(accounts.FirstOrDefault())
  225. .WithPrompt(Prompt.SelectAccount)
  226. .ExecuteAsync()
  227. .ConfigureAwait(false)
  228. .GetAwaiter()
  229. .GetResult();
  230. }
  231. catch (Exception ex)
  232. {
  233. SetError(1, "AcquireTokenById.Login Failed.", ex.Message);
  234. return null;
  235. }
  236. return result.AccessToken;
  237. }
  238. public async Task<AuthenticationResult> AcquireTokenAsync(string authority, string resource, string claims)
  239. {
  240. AuthenticationResult result = null;
  241. if (authority.ToLower().Contains("common"))
  242. {
  243. var authorityUri = new Uri(authority);
  244. authority = String.Format("https://{0}/{1}", authorityUri.Host, _aipConfig.TenantId);
  245. }
  246. var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
  247. .WithAuthority(authority)
  248. .WithDefaultRedirectUri()
  249. .Build();
  250. var accounts = (app.GetAccountsAsync()).GetAwaiter().GetResult();
  251. // Append .default to the resource passed in to AcquireToken().
  252. string[] scopes = { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  253. try
  254. {
  255. result = await app.AcquireTokenSilent(new[] { "https://aadrm.com/user_impersonation" }, accounts.FirstOrDefault()).ExecuteAsync();
  256. // result = await _app.AcquireTokenSilent(scopes,
  257. // accounts.FirstOrDefault())
  258. // .ExecuteAsync();
  259. }
  260. catch (MsalUiRequiredException)
  261. {
  262. result = app.AcquireTokenInteractive(scopes)
  263. .WithAccount(accounts.FirstOrDefault())
  264. .WithPrompt(Prompt.SelectAccount)
  265. .ExecuteAsync()
  266. .ConfigureAwait(false)
  267. .GetAwaiter()
  268. .GetResult();
  269. }
  270. // Return the token. The token is sent to the resource.
  271. return result;
  272. }
  273. // public Identity GetUserIdentity()
  274. // {
  275. // try
  276. // {
  277. // string resource = "https://graph.microsoft.com/";
  278. //
  279. // AuthenticationContext authContext = new AuthenticationContext("https://login.windows.net/common", tokenCache);
  280. // var result = authContext.AcquireTokenAsync(resource, appInfo.ApplicationId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Always)).Result;
  281. // return new Identity(result.UserInfo.DisplayableId);
  282. //
  283. // }
  284. // catch (Exception ex)
  285. // {
  286. // throw ex;
  287. // }
  288. // }
  289. // private async Task<string> GetAccessTokenOnBehalfOfUser(string authority, string resource)
  290. // {
  291. // IConfidentialClientApplication app;
  292. //
  293. // if (false)
  294. // {
  295. // // Read X509 cert from local store and build ClientAssertionCertificate.
  296. // X509Certificate2 cert = Utilities.ReadCertificateFromStore(_aipConfig.CertThumbPrint);
  297. //
  298. // // Create confidential client using certificate.
  299. // app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  300. // .WithRedirectUri(resource)
  301. // .WithAuthority(authority)
  302. // .WithCertificate(cert)
  303. // .Build();
  304. // }
  305. //
  306. // else
  307. // {
  308. // // Create confidential client using client secret.
  309. // app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
  310. // .WithRedirectUri(resource)
  311. // .WithAuthority(authority)
  312. // .WithClientSecret(_aipConfig.SecretValue)
  313. // .Build();
  314. // }
  315. //
  316. // // Store user access token of authenticated user.
  317. // var ci = (ClaimsIdentity)_claimsPrincipal.Identity;
  318. // string userAccessToken = (string)ci.BootstrapContext;
  319. //
  320. //
  321. // // Generate a user assertion with the UPN and access token.
  322. // UserAssertion userAssertion = new UserAssertion(userAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer");
  323. //
  324. // // Append .default to the resource passed in to AcquireToken().
  325. // List<string> scopes = new List<string>() { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
  326. //
  327. // AuthenticationResult result = await app.AcquireTokenOnBehalfOf(scopes, userAssertion)
  328. // .ExecuteAsync();
  329. //
  330. // // Return the token to the API caller
  331. // return (result.AccessToken);
  332. // }
  333. }
  334. }