123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- using System;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using System.Security.Cryptography.X509Certificates;
- using System.Threading.Tasks;
- using Microsoft.Identity.Client;
- using Microsoft.Identity.Client.Extensions.Msal;
- using Microsoft.InformationProtection;
- namespace AipGateway.AIP
- {
- public class AuthDelegateImplementation : IAuthDelegate
- {
- public int LastErrNo { get; set; }
- public string LastErrMsg { get; set; }
- private readonly AipConfig _aipConfig;
- private readonly IConfidentialClientApplication _confidentialApp = null;
- private readonly IPublicClientApplication _publicApp = null;
- public AuthDelegateImplementation(AipConfig aipConfig)
- {
- _aipConfig = aipConfig;
- LastErrNo = 0;
- LastErrMsg = String.Empty;
- var storageProperties =
- new StorageCreationPropertiesBuilder(
- "AIPGateway.Cache",
- Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
- .Build();
- var cacheHelper = MsalCacheHelper.CreateAsync(storageProperties).GetAwaiter().GetResult();
- if (_aipConfig.LoginType == AipAuthLoginType.authLoginPassword)
- {
- _confidentialApp = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- .WithClientSecret(_aipConfig.SecretValue)
- // .WithAuthority($"https://login.microsoftonline.com/{_aipConfig.TenantId}")
- // .WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
- .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
- .Build();
- cacheHelper.RegisterCache(_confidentialApp.UserTokenCache);
- }
- else if (_aipConfig.LoginType == AipAuthLoginType.authLoginCert)
- {
- var authority = $"https://login.windows.net/{_aipConfig.TenantId}";
- // var certificate = Utilities.ReadCertificateFromStore(_aipConfig.CertThumbPrint);
- var myCertificate = X509Certificate2.CreateFromCertFile(_aipConfig.CertThumbPrint);
- X509Certificate2 certificate = new X509Certificate2(myCertificate);
- _confidentialApp = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- .WithCertificate(certificate)
- .WithAuthority(new Uri(authority))
- .Build();
- cacheHelper.RegisterCache(_confidentialApp.UserTokenCache);
- }
- else
- {
- _publicApp = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
- .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
- .WithAuthority(AzureCloudInstance.AzurePublic, _aipConfig.TenantId)
- .Build();
- cacheHelper.RegisterCache(_publicApp.UserTokenCache);
- }
- }
- public void ResetError()
- {
- LastErrNo = 0;
- LastErrMsg = string.Empty;
- }
- private void SetError(int errNo, string errMsg1, string errMsg2 = "No Exception Message.")
- {
- LastErrNo = errNo;
- LastErrMsg = errMsg1 + "\r\n" + errMsg2;
- Console.WriteLine("AuthDelegateImplementation::SetError ==> {0}, {1}, {2}", errNo, errMsg1, errMsg2);
- }
- public string AcquireToken(Identity identity, string authority, string resource, string claim)
- {
- Console.WriteLine("AuthDelegateImplementation::AcquireToken ==> LoginType: {0}", _aipConfig.LoginType);
-
- // var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithAuthority(authority)
- // .Build();
- //
- // var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
- // try
- // {
- // var accounts = app.GetAccountsAsync().GetAwaiter().GetResult();
- // var result = app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
- // .ExecuteAsync().Result;
- //
- // Console.WriteLine("AuthDelegateImplementation::AcquireToken ==> AcquireTokenSilent Succeed.");
- // return result.AccessToken;
- // }
- // catch (MsalUiRequiredException)
- // {
- // Console.WriteLine("AuthDelegateImplementation::AcquireToken ==> AcquireTokenSilent Failed.");
- // }
- if (_aipConfig.LoginType == AipAuthLoginType.authLoginPassword)
- {
- return AcquireTokenByPassword(identity, authority, resource, claim);
- }
-
- if (_aipConfig.LoginType == AipAuthLoginType.authLoginCert)
- {
- return AcquireTokenByCert(identity, authority, resource, claim);
- }
- return AcquireTokenById(identity, authority, resource, claim);
- }
- private string AcquireTokenByPassword(Identity identity, string authority, string resource, string claims)
- {
- Console.WriteLine("AuthDelegateImplementation::AcquireTokenByPassword");
- AuthenticationResult result;
- var authorityUri = new Uri(authority);
- authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
-
- // var app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithClientSecret(_aipConfig.SecretValue)
- // // .WithAuthority($"https://login.microsoftonline.com/{_aipConfig.TenantId}")
- // // .WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
- // .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
- // .Build();
- // Create confidential client using client secret.
- // var app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithRedirectUri(resource)
- // .WithAuthority(authority)
- // .WithClientSecret(_aipConfig.SecretValue)
- // .Build();
- var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
- //app.AddInMemoryTokenCache();
- try
- {
- var accounts = (_confidentialApp.GetAccountsAsync()).GetAwaiter().GetResult();
- result = _confidentialApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
- .ExecuteAsync().GetAwaiter().GetResult();
- Console.WriteLine("AuthDelegateImplementation::AcquireTokenByPassword ==> AcquireTokenSilent Succeed.");
- return result.AccessToken;
- }
- catch (MsalUiRequiredException)
- {
- Console.WriteLine("AuthDelegateImplementation::AcquireTokenByPassword ==> AcquireTokenSilent Failed.");
- try
- {
- result = _confidentialApp.AcquireTokenForClient(scopes)
- .WithTenantId(_aipConfig.TenantId)
- .ExecuteAsync()
- .GetAwaiter()
- .GetResult();
- }
- catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
- {
- // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
- // Mitigation: change the scope to be as expected
- SetError(1, "AcquireTokenByPassword.Scope provided is not supported.", ex.Message);
- return null;
- }
- }
- return result.AccessToken;
- }
- private string AcquireTokenByCert(Identity identity, string authority, string resource, string claims)
- {
- Console.WriteLine("AuthDelegateImplementation::AcquireTokenByPassword");
- AuthenticationResult result;
- var authorityUri = new Uri(authority);
- authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
- // Read cert from local machine
- // var certificate = Utilities.ReadCertificateFromStore(_aipConfig.CertThumbPrint);
- var myCertificate = X509Certificate2.CreateFromCertFile(_aipConfig.CertThumbPrint);
- X509Certificate2 certificate = new X509Certificate2(myCertificate);
- // Use cert to build ClientAssertionCertificate
- // var app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithCertificate(certificate)
- // .WithAuthority(new Uri(authority))
- // .Build();
- // var app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithCertificate(certificate)
- // .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
- // .Build();
- // Append .default to the resource passed in to AcquireToken().
- var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
- try
- {
- result = _confidentialApp.AcquireTokenForClient(scopes).ExecuteAsync().Result;
- }
- catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
- {
- // Invalid scope. The scope has to be of the form "https://resourceurl/.default"
- // Mitigation: change the scope to be as expected
- SetError(1, "AcquireTokenByCert.Scope provided is not supported.", ex.Message);
- return null;
- }
- return result.AccessToken;
- }
- private string AcquireTokenById(Identity identity, string authority, string resource, string claims)
- {
- Console.WriteLine("AcquireTokenById::AcquireTokenByPassword");
- AuthenticationResult result;
- var authorityUri = new Uri(authority);
- authority = $"https://{authorityUri.Host}/{_aipConfig.TenantId}";
- // var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
- // .WithAuthority(AzureCloudInstance.AzurePublic, _aipConfig.TenantId)
- // .Build();
- var accounts = (_publicApp.GetAccountsAsync()).GetAwaiter().GetResult();
- // Append .default to the resource passed in to AcquireToken().
- var scopes = new[] { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
- try
- {
- result = _publicApp.AcquireTokenInteractive(scopes)
- .WithAccount(accounts.FirstOrDefault())
- .WithPrompt(Prompt.SelectAccount)
- .ExecuteAsync()
- .ConfigureAwait(false)
- .GetAwaiter()
- .GetResult();
- }
- catch (Exception ex)
- {
- SetError(1, "AcquireTokenById.Login Failed.", ex.Message);
- return null;
- }
- return result.AccessToken;
- }
- // public X509Certificate2 LoadCertificate(string certificateName)
- // {
- // string vaultUrl = "";
- // string clientId = _aipConfig.ClientId;
- // string tenantId = _aipConfig.TenantId;
- // string secret = "";
- //
- // Console.WriteLine($"Loading certificate '{certificateName}' from Azure Key Vault");
- //
- // var credentials = new ClientSecretCredential(tenantId: tenantId, clientId: clientId, clientSecret: secret);
- // var certClient = new CertificateClient(new Uri(vaultUrl), credentials);
- // var secretClient = new SecretClient(new Uri(vaultUrl), credentials);
- //
- // var cert = GetCertificateAsync(certClient, secretClient, certificateName);
- //
- // Console.WriteLine("Certificate loaded");
- // return cert;
- // }
- // private static X509Certificate2 GetCertificateAsync(CertificateClient certificateClient,
- // SecretClient secretClient,
- // string certificateName)
- // {
- //
- // KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);
- //
- // // Return a certificate with only the public key if the private key is not exportable.
- // if (certificate.Policy?.Exportable != true)
- // {
- // return new X509Certificate2(certificate.Cer);
- // }
- //
- // // Parse the secret ID and version to retrieve the private key.
- // string[] segments = certificate.SecretId.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
- // if (segments.Length != 3)
- // {
- // throw new InvalidOperationException($"Number of segments is incorrect: {segments.Length}, URI: {certificate.SecretId}");
- // }
- //
- // string secretName = segments[1];
- // string secretVersion = segments[2];
- //
- // KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion);
- //
- // // For PEM, you'll need to extract the base64-encoded message body.
- // // .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
- // if ("application/x-pkcs12".Equals(secret.Properties.ContentType, StringComparison.InvariantCultureIgnoreCase))
- // {
- // byte[] pfx = Convert.FromBase64String(secret.Value);
- // return new X509Certificate2(pfx);
- // }
- //
- // throw new NotSupportedException($"Only PKCS#12 is supported. Found Content-Type: {secret.Properties.ContentType}");
- // }
- // }
- private X509Certificate2 ReadCertificateFromStore(string thumbprint)
- {
- X509Certificate2 cert = null;
- X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
- store.Open(OpenFlags.ReadOnly);
- X509Certificate2Collection certCollection = store.Certificates;
- // Find unexpired certificates.
- X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
- // From the collection of unexpired certificates, find the ones with the correct name.
- X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindByThumbprint, thumbprint, false);
- // Return the first certificate in the collection, has the right name and is current.
- cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
- store.Close();
- return cert;
- }
- public async Task<AuthenticationResult> AcquireTokenAsync(string authority, string resource, string claims)
- {
- AuthenticationResult result = null;
- if (authority.ToLower().Contains("common"))
- {
- var authorityUri = new Uri(authority);
- authority = String.Format("https://{0}/{1}", authorityUri.Host, _aipConfig.TenantId);
- }
-
- var app = PublicClientApplicationBuilder.Create(_aipConfig.ClientId)
- .WithAuthority(authority)
- .WithDefaultRedirectUri()
- .Build();
- var accounts = (app.GetAccountsAsync()).GetAwaiter().GetResult();
- // Append .default to the resource passed in to AcquireToken().
- string[] scopes = { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
- try
- {
- result = await app.AcquireTokenSilent(new[] { "https://aadrm.com/user_impersonation" }, accounts.FirstOrDefault()).ExecuteAsync();
- // result = await _app.AcquireTokenSilent(scopes,
- // accounts.FirstOrDefault())
- // .ExecuteAsync();
- }
- catch (MsalUiRequiredException)
- {
- result = app.AcquireTokenInteractive(scopes)
- .WithAccount(accounts.FirstOrDefault())
- .WithPrompt(Prompt.SelectAccount)
- .ExecuteAsync()
- .ConfigureAwait(false)
- .GetAwaiter()
- .GetResult();
- }
- // Return the token. The token is sent to the resource.
- return result;
- }
- // private async Task<string> GetAccessTokenOnBehalfOfUser(string authority, string resource)
- // {
- // IConfidentialClientApplication app;
- //
- // if (false)
- // {
- // // Read X509 cert from local store and build ClientAssertionCertificate.
- // X509Certificate2 cert = Utilities.ReadCertificateFromStore(_aipConfig.CertThumbPrint);
- //
- // // Create confidential client using certificate.
- // app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithRedirectUri(resource)
- // .WithAuthority(authority)
- // .WithCertificate(cert)
- // .Build();
- // }
- //
- // else
- // {
- // // Create confidential client using client secret.
- // app = ConfidentialClientApplicationBuilder.Create(_aipConfig.ClientId)
- // .WithRedirectUri(resource)
- // .WithAuthority(authority)
- // .WithClientSecret(_aipConfig.SecretValue)
- // .Build();
- // }
- //
- // // Store user access token of authenticated user.
- // var ci = (ClaimsIdentity)_claimsPrincipal.Identity;
- // string userAccessToken = (string)ci.BootstrapContext;
- //
- //
- // // Generate a user assertion with the UPN and access token.
- // UserAssertion userAssertion = new UserAssertion(userAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer");
- //
- // // Append .default to the resource passed in to AcquireToken().
- // List<string> scopes = new List<string>() { resource[resource.Length - 1].Equals('/') ? $"{resource}.default" : $"{resource}/.default" };
- //
- // AuthenticationResult result = await app.AcquireTokenOnBehalfOf(scopes, userAssertion)
- // .ExecuteAsync();
- //
- // // Return the token to the API caller
- // return (result.AccessToken);
- // }
- }
- }
|