diff --git a/Directory.Packages.props b/Directory.Packages.props index d523737aa9..a75cac1ef8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ 0.16.2 - 4.61.0 + 4.99.0-internal @@ -40,8 +40,10 @@ - - + + + + diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs index e954ed2d12..bf39010a27 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs @@ -211,7 +211,7 @@ private async Task TryGetTokenUsingFociAsync(CancellationToke { // Hack: STS does not yet send back the suberror on these platforms because they are not in an allowed list, // so the best thing we can do is to consider all errors as client_mismatch. -#if NETSTANDARD || MAC +#if NETSTANDARD ex?.GetType(); // avoid the "variable 'ex' is declared but never used" in this code path. return null; #else diff --git a/tests/CacheCompat/CommonCache.Test.Unit/CacheExecutionTests.cs b/tests/CacheCompat/CommonCache.Test.Unit/CacheExecutionTests.cs index dcb4ad8e6e..172c64629b 100644 --- a/tests/CacheCompat/CommonCache.Test.Unit/CacheExecutionTests.cs +++ b/tests/CacheCompat/CommonCache.Test.Unit/CacheExecutionTests.cs @@ -19,8 +19,8 @@ public class CacheExecutionTests private static async Task GetPublicAadUserDataAsync() { - var api = new LabServiceApi(); - LabResponse labResponse = (await api.GetLabResponseFromApiAsync(UserQuery.PublicAadUserQuery).ConfigureAwait(false)); + LabResponse labResponse = await LabUserHelper.GetDefaultUserAsync().ConfigureAwait(false); + return new LabUserData( labResponse.User.Upn, labResponse.User.GetOrFetchPassword(), diff --git a/tests/Microsoft.Identity.Test.Common/Core/Helpers/CertHelper.cs b/tests/Microsoft.Identity.Test.Common/Core/Helpers/CertHelper.cs new file mode 100644 index 0000000000..568746734a --- /dev/null +++ b/tests/Microsoft.Identity.Test.Common/Core/Helpers/CertHelper.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace Microsoft.Identity.Test.Common.Core.Helpers +{ + public static class CertificateFinder + { + /// + /// Try and locate a certificate matching the given by searching in + /// the store subjectName for all available s. + /// + /// Thumbprint of certificate to locate + /// with , or null if no matching certificate was found + public static X509Certificate2 FindCertificateByName(string subjectName) + { + foreach (StoreLocation storeLocation in Enum.GetValues(typeof(StoreLocation))) + { + var certificate = FindCertificateByName(subjectName, storeLocation, StoreName.My); + if (certificate != null) + { + return certificate; + } + } + + return null; + } + /// + /// Try and locate a certificate matching the given by searching in + /// the in the given and . + /// + /// Thumbprint of certificate to locate + /// in which to search for a matching certificate + /// in which to search for a matching certificate + /// with , or null if no matching certificate was found + public static X509Certificate2 FindCertificateByName(string certName, StoreLocation location, StoreName name) + { + // Don't validate certs, since the test root isn't installed. + const bool validateCerts = false; + + using (var store = new X509Store(name, location)) + { + store.Open(OpenFlags.ReadOnly); + X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, certName, validateCerts); + + X509Certificate2 certToUse = null; + + // select the "freshest" certificate + foreach (X509Certificate2 cert in collection) + { + if (certToUse == null || cert.NotBefore > certToUse.NotBefore) + { + certToUse = cert; + } + } + + return certToUse; + + } + } + } + + public enum KnownTestCertType + { + RSA, + ECD + } +} diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/CiamIntegrationTests.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/CiamIntegrationTests.cs index 02076ed760..6affcf562b 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/CiamIntegrationTests.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/CiamIntegrationTests.cs @@ -32,11 +32,7 @@ public async Task ROPC_Ciam_StandardDomains_CompletesSuccessfully() { string authority; //Get lab details - var labResponse = await LabUserHelper.GetLabUserDataAsync(new UserQuery() - { - FederationProvider = FederationProvider.CIAMCUD, - SignInAudience = SignInAudience.AzureAdMyOrg - }).ConfigureAwait(false); + var labResponse = await LabUserHelper.GetCiamUserAync().ConfigureAwait(false); //https://tenantName.ciamlogin.com/ authority = string.Format("https://{0}.ciamlogin.com/", labResponse.User.LabName); @@ -87,12 +83,7 @@ public async Task ClientCredentialCiam_WithClientCredentials_ReturnsValidTokens( { string authority; //Get lab details - var labResponse = await LabUserHelper.GetLabUserDataAsync(new UserQuery() - { - FederationProvider = FederationProvider.CIAMCUD, - SignInAudience = SignInAudience.AzureAdMyOrg - }).ConfigureAwait(false); - + var labResponse = await LabUserHelper.GetCiamUserAync().ConfigureAwait(false); //https://tenantName.ciamlogin.com/ authority = string.Format("https://{0}.ciamlogin.com/", labResponse.User.LabName); @@ -117,7 +108,7 @@ private async Task RunCiamCCATest(string authority, string appId) //Acquire tokens var msalConfidentialClientBuilder = ConfidentialClientApplicationBuilder .Create(appId) - .WithCertificate(CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName)) + .WithCertificate(CertificateFinder.FindCertificateByName(TestConstants.AutomationTestCertName)) .WithExperimentalFeatures(); if (authority.Contains(Constants.CiamAuthorityHostSuffix)) @@ -157,11 +148,7 @@ public async Task OBOCiam_CustomDomain_ReturnsValidTokens() string ciamWebApi = "634de702-3173-4a71-b336-a4fab786a479"; //Get lab details - LabResponse labResponse = await LabUserHelper.GetLabUserDataAsync(new UserQuery() - { - FederationProvider = FederationProvider.CIAMCUD, - SignInAudience = SignInAudience.AzureAdMyOrg - }).ConfigureAwait(false); + var labResponse = await LabUserHelper.GetCiamUserAync().ConfigureAwait(false); //Acquire tokens var msalPublicClient = PublicClientApplicationBuilder @@ -184,7 +171,7 @@ public async Task OBOCiam_CustomDomain_ReturnsValidTokens() //Acquire tokens for OBO var msalConfidentialClient = ConfidentialClientApplicationBuilder .Create(ciamWebApi) - .WithCertificate(CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName)) + .WithCertificate(CertificateFinder.FindCertificateByName(TestConstants.AutomationTestCertName)) .WithAuthority(authorityCud, false) .WithRedirectUri(_ciamRedirectUri) .BuildConcrete(); @@ -212,11 +199,5 @@ public async Task OBOCiam_CustomDomain_ReturnsValidTokens() Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.Cache, resultObo.AuthenticationResultMetadata.TokenSource); } - - private string GetCiamSecret() - { - KeyVaultSecretsProvider provider = new KeyVaultSecretsProvider(); - return provider.GetSecretByName("msidlabciam2-cc").Value; - } } } diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs index 27e443c9b3..839873bc5c 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/ClientCredentialsTests.NetFwk.cs @@ -64,11 +64,11 @@ public async Task RefreshOnIsEnabled(bool useRegional) Assert.Inconclusive("Can't run regional on local devbox."); } - var cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + var cert = CertificateFinder.FindCertificateByName(TestConstants.AutomationTestCertName); - var builder = ConfidentialClientApplicationBuilder.Create(LabAuthenticationHelper.LabAccessConfidentialClientId) + var builder = ConfidentialClientApplicationBuilder.Create(LabApiConstants.LabClientId) .WithCertificate(cert, sendX5C: true) - .WithAuthority(LabAuthenticationHelper.LabClientInstance, LabAuthenticationHelper.LabClientTenantId); + .WithAuthority(LabApiConstants.LabClientInstance, LabApiConstants.LabClientTenantId); // auto-detect should work on Azure DevOps build if (useRegional) @@ -76,7 +76,7 @@ public async Task RefreshOnIsEnabled(bool useRegional) var cca = builder.Build(); - var result = await cca.AcquireTokenForClient([LabAuthenticationHelper.LabScope]).ExecuteAsync().ConfigureAwait(false); + var result = await cca.AcquireTokenForClient([LabApiConstants.LabScope]).ExecuteAsync().ConfigureAwait(false); Assert.AreEqual(TokenSource.IdentityProvider, result.AuthenticationResultMetadata.TokenSource); Assert.IsTrue(result.AuthenticationResultMetadata.RefreshOn.HasValue, "refresh_in was not issued - did the MSAL SKU value change?"); diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs index b60c70f865..6bf263d701 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/HeadlessTests/PoPTests.NetFwk.cs @@ -770,7 +770,7 @@ public void CheckPopRuntimeBrokerSupportTest() private static X509Certificate2 GetCertificate() { - X509Certificate2 cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); + X509Certificate2 cert = CertificateFinder.FindCertificateByName(TestConstants.AutomationTestCertName); if (cert == null) { diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/ConfidentialAppSettings.cs b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/ConfidentialAppSettings.cs index db63ff07bd..f9054561f1 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/ConfidentialAppSettings.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/ConfidentialAppSettings.cs @@ -3,6 +3,7 @@ using System; using System.Security.Cryptography.X509Certificates; +using Microsoft.Identity.Test.Common.Core.Helpers; using Microsoft.Identity.Test.LabInfrastructure; using Microsoft.Identity.Test.Unit; @@ -196,7 +197,7 @@ public static IConfidentialAppSettings GetSettings(Cloud cloud) public static Lazy GetCertificateLazy(string certName) => new Lazy(() => { - X509Certificate2 cert = CertificateHelper.FindCertificateByName(certName); + X509Certificate2 cert = CertificateFinder.FindCertificateByName(certName); if (cert == null) { throw new InvalidOperationException( diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsiProxyHttpManager.cs b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsiProxyHttpManager.cs index aada0b10d4..8ca37e143a 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsiProxyHttpManager.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/MsiProxyHttpManager.cs @@ -3,16 +3,12 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Web; -using Microsoft.Identity.Client; using Microsoft.Identity.Client.Core; using Microsoft.Identity.Client.Http; using Microsoft.Identity.Test.LabInfrastructure; diff --git a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/PoPValidator.cs b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/PoPValidator.cs index e92822ddc7..8cdfa5927c 100644 --- a/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/PoPValidator.cs +++ b/tests/Microsoft.Identity.Test.Integration.netcore/Infrastructure/PoPValidator.cs @@ -17,12 +17,7 @@ namespace Microsoft.Identity.Test.Integration.Infrastructure public class PoPValidator { // This endpoint is hosted in the MSID Lab and is able to verify any pop token bound to an HTTP request - private const string PoPValidatorEndpoint = "https://signedhttprequest.azurewebsites.net/api/validateSHR"; private static HttpClient s_httpClient = new HttpClient(); - private static Lazy s_popValidationEndpointLazy = new Lazy( - () => LabUserHelper.KeyVaultSecretsProviderMsal.GetSecretByName( - "automation-pop-validation-endpoint", - "841fc7c2ccdd48d7a9ef727e4ae84325").Value); /// /// This calls a special endpoint that validates any POP token against a configurable HTTP request. diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/CertificateHelper.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/CertificateHelper.cs index 1c4519e621..c06788d1ec 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/CertificateHelper.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/CertificateHelper.cs @@ -6,7 +6,7 @@ namespace Microsoft.Identity.Test.LabInfrastructure { - public static class CertificateHelper + internal static class CertificateHelper { /// /// Try and locate a certificate matching the given by searching in diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultConfiguration.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultConfiguration.cs deleted file mode 100644 index 4a274cfe93..0000000000 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultConfiguration.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Identity.Test.LabInfrastructure -{ - public class KeyVaultConfiguration - { - /// - /// The URL of the Key Vault instance. - /// - public string Url { get; set; } - - /// - /// The authentication type to use to communicate with the Key Vault. - /// - public LabAccessAuthenticationType AuthType { get; set; } - - /// - /// The ID of the test harness client. - /// - /// - /// This should be configured as having access to the Key Vault instance specified at . - /// - public string ClientId { get; set; } - - /// - /// The thumbprint of the to use when - /// is . - /// - public string CertThumbprint { get; set; } - - /// - /// Secret value used to access Key Vault - /// - public string KeyVaultSecret { get; set; } - } - -} diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultSecretsProvider.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultSecretsProvider.cs index a15ce42636..8d3993db44 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultSecretsProvider.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/KeyVaultSecretsProvider.cs @@ -24,7 +24,7 @@ public class KeyVaultInstance public const string MsalTeam = "https://buildautomation.vault.azure.net/"; } - public class KeyVaultSecretsProvider : IDisposable + public class KeyVaultSecretsProvider { private CertificateClient _certificateClient; private SecretClient _secretClient; @@ -58,43 +58,20 @@ public class KeyVaultSecretsProvider : IDisposable /// public KeyVaultSecretsProvider(string keyVaultAddress = KeyVaultInstance.MSIDLab) { - var credentials = GetKeyVaultCredentialAsync().GetAwaiter().GetResult(); + var credential = LabAuthenticationHelper.GetTokenCredential(); var keyVaultAddressUri = new Uri(keyVaultAddress); - _certificateClient = new CertificateClient(keyVaultAddressUri, credentials); - _secretClient = new SecretClient(keyVaultAddressUri, credentials); - } - - ~KeyVaultSecretsProvider() - { - Dispose(); + _certificateClient = new CertificateClient(keyVaultAddressUri, credential); + _secretClient = new SecretClient(keyVaultAddressUri, credential); } public KeyVaultSecret GetSecretByName(string secretName) { return _secretClient.GetSecret(secretName).Value; } - - public KeyVaultSecret GetSecretByName(string secretName, string secretVersion) - { - return _secretClient.GetSecret(secretName, secretVersion).Value; - } - + public async Task GetCertificateWithPrivateMaterialAsync(string certName) { return await _certificateClient.DownloadCertificateAsync(certName).ConfigureAwait(false); - } - - private async Task GetKeyVaultCredentialAsync() - { - var accessToken = await LabAuthenticationHelper.GetLabAccessTokenAsync( - "https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/", - new[] { "https://vault.azure.net/.default" }).ConfigureAwait(false); - return DelegatedTokenCredential.Create((_, __) => accessToken); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - } + } } } diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabApiConstants.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabApiConstants.cs index 14338c9e2e..690a468887 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabApiConstants.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabApiConstants.cs @@ -3,7 +3,24 @@ namespace Microsoft.Identity.Test.LabInfrastructure { - public class LabApiConstants + public static class LabApiConstants + { + //public const string LabClientId = "f62c5ae3-bf3a-4af5-afa8-a68b800396e9"; + //public const string LabScope = "https://request.msidlab.com/.default"; + + // IdentityLabsOneAccess + //public const string LabClientId = "00bedee1-0e09-4a8d-81a0-0679c5a64a83"; + //public const string LabScope = "https://request.msidlab.com/.default"; + + + public const string LabClientId = "5741af7e-6c00-4ed9-b589-73366d58a430"; + public const string LabScope = "https://request.msidlab.com/.default"; + + public const string LabClientInstance = "https://login.microsoftonline.com/"; + public const string LabClientTenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; + } + + internal static class InternalConstants { // constants for Lab api public const string MobileDeviceManagementWithConditionalAccess = "mdmca"; diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabAuthenticationHelper.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabAuthenticationHelper.cs index abc5671352..3bd9eea8c5 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabAuthenticationHelper.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabAuthenticationHelper.cs @@ -2,78 +2,99 @@ // Licensed under the MIT License. using System; -using System.IO; -using System.Security.Cryptography.X509Certificates; -using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Microsoft.Identity.Client; -using Microsoft.Identity.Test.Unit; -using Microsoft.Identity.Test.Common.Core.Mocks; +using Azure.Identity; +using System.Runtime.InteropServices; +using Azure.Identity.Broker; +using System.Diagnostics; namespace Microsoft.Identity.Test.LabInfrastructure { - public static class LabAuthenticationHelper + internal static class LabAuthenticationHelper { - public const string LabAccessConfidentialClientId = "f62c5ae3-bf3a-4af5-afa8-a68b800396e9"; - public const string LabScope = "https://request.msidlab.com/.default"; - public const string LabClientInstance = "https://login.microsoftonline.com/"; - public const string LabClientTenantId = "72f988bf-86f1-41af-91ab-2d7cd011db47"; + public static async Task GetAccessTokenForLabAPIAsync() + { + var tokenCredential = GetTokenCredential(); + + return await tokenCredential + .GetTokenAsync(new TokenRequestContext([LabApiConstants.LabScope]), default) + .ConfigureAwait(false); + } - public static async Task GetAccessTokenForLabAPIAsync(string labAccessClientId) + public static async Task GetKeyVaultAccessToken() { - string[] scopes = new string[] { LabScope }; + var tokenCredential = GetTokenCredential(); - return await GetLabAccessTokenAsync( - LabClientInstance + LabClientTenantId, - scopes, - labAccessClientId).ConfigureAwait(false); + return await tokenCredential + .GetTokenAsync(new TokenRequestContext(["https://vault.azure.net/.default"]), default) + .ConfigureAwait(false); } - public static async Task GetLabAccessTokenAsync(string authority, string[] scopes) + internal static TokenCredential GetTokenCredential() { - return await GetLabAccessTokenAsync( - authority, - scopes, - String.Empty).ConfigureAwait(false); + TokenCredential tokenCredential; + if (Environment.GetEnvironmentVariable("TF_BUILD") == null) + { + Debug.WriteLine("[LabAPI] Not on CI, using interactive browser/broker credential"); + tokenCredential = GetAzureCredentialForDevBox(); + } + else + { + Debug.WriteLine("[LabAPI] On CI, using ADO federation"); + tokenCredential = GetAzureCredentialForCI(); + } + + return tokenCredential; } - public static async Task GetLabAccessTokenAsync(string authority, string[] scopes, string clientId) + private static TokenCredential GetAzureCredentialForCI() { - AuthenticationResult authResult; - IConfidentialClientApplication confidentialApp; - X509Certificate2 cert; + // as per https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/samples/OtherCredentialSamples.md#authenticating-in-azure-pipelines-with-service-connections - var clientIdForCertAuth = String.IsNullOrEmpty(clientId) ? LabAccessConfidentialClientId : clientId; + string clientId = "4b7a4b0b-ecb2-409e-879a-1e21a15ddaf6"; // UAMI client ID + string tenantId = LabApiConstants.LabClientTenantId; + string serviceConnectionId = "6eeeb73d-37aa-4d78-83b7-728101b8bddd"; - cert = CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName); - if (cert == null) - { - throw new InvalidOperationException( - "Test setup error - cannot find a certificate in the My store for KeyVault. This is available for Microsoft employees only."); - } + var pipelinesCredential = new AzurePipelinesCredential( + tenantId, + clientId, + serviceConnectionId, + Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN"), + new AzurePipelinesCredentialOptions() + { + TokenCachePersistenceOptions = new TokenCachePersistenceOptions() + { + Name = "MSIDLabTokenCache", + UnsafeAllowUnencryptedStorage = true // We generally use headless Linux, so cannot use LibSecret. This is the ~same level of protection as SSH keys. + } + }); + + return pipelinesCredential; + } - confidentialApp = ConfidentialClientApplicationBuilder - .Create(clientIdForCertAuth) - .WithAuthority(new Uri(authority), true) - .WithCacheOptions(CacheOptions.EnableSharedCacheOptions) - .WithCertificate(cert, true) - .Build(); + // TODO: test this on MacOs / Linux WSL + private static TokenCredential GetAzureCredentialForDevBox() + { + InteractiveBrowserCredential interactiveBrowserCredential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialBrokerOptions(GetForegroundWindow()) + { + ClientId = LabApiConstants.LabClientId, + TenantId = LabApiConstants.LabClientTenantId, + TokenCachePersistenceOptions = new TokenCachePersistenceOptions() + { + Name = "MSIDLabTokenCache", + UnsafeAllowUnencryptedStorage = true // We generally use headless Linux, so cannot use LibSecret. This is the ~same level of protection as SSH keys. + }, + RedirectUri = new Uri("http://localhost"), // On Mac and Linux, MSAL will fallback to browser + UseDefaultBrokerAccount = true // - authResult = await confidentialApp - .AcquireTokenForClient(scopes) - .WithSendX5C(true) - .ExecuteAsync(CancellationToken.None) - .ConfigureAwait(false); + }); - return new AccessToken(authResult.AccessToken, authResult.ExpiresOn); + return interactiveBrowserCredential; } - } - public enum LabAccessAuthenticationType - { - ClientCertificate, - ClientSecret, - UserCredential + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); } } diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceApi.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceApi.cs index 840412c50d..241a17803b 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceApi.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceApi.cs @@ -15,16 +15,25 @@ namespace Microsoft.Identity.Test.LabInfrastructure /// /// Wrapper for new lab service API /// - public class LabServiceApi + public class LabServiceApi { - private string _labAccessAppId; private AccessToken? _labApiAccessToken; private AccessToken? _msiHelperApiAccessToken; public LabServiceApi() { - KeyVaultSecretsProvider _keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - _labAccessAppId = _keyVaultSecretsProvider.GetSecretByName("LabVaultAppID").Value; + } + + public async Task GetMSIHelperServiceTokenAsync() + { + if (_msiHelperApiAccessToken == null) + { + _msiHelperApiAccessToken = await LabAuthenticationHelper + .GetAccessTokenForLabAPIAsync() + .ConfigureAwait(false); + } + + return _msiHelperApiAccessToken.Value.Token; } /// @@ -33,14 +42,15 @@ public LabServiceApi() /// Any and all parameters that the returned user should satisfy. /// Users that match the given query parameters. - public async Task GetLabResponseFromApiAsync(UserQuery query) + internal async Task GetLabResponseFromApiAsync(UserQuery query) { //Fetch user string result = await RunQueryAsync(query).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(result)) { - throw new LabUserNotFoundException(query, "No lab user with specified parameters exists"); + throw new LabUserNotFoundException( + "No lab user with specified parameters exists: " + query.ToString()); } return CreateLabResponseFromResultStringAsync(result).Result; @@ -52,10 +62,10 @@ internal async Task CreateLabResponseFromResultStringAsync(string r var user = userResponses[0]; - var appResponse = await GetLabResponseAsync(LabApiConstants.LabAppEndpoint + user.AppId).ConfigureAwait(false); + var appResponse = await GetLabResponseAsync(InternalConstants.LabAppEndpoint + user.AppId).ConfigureAwait(false); LabApp[] labApps = JsonConvert.DeserializeObject(appResponse); - var labInfoResponse = await GetLabResponseAsync(LabApiConstants.LabInfoEndpoint + user.LabName).ConfigureAwait(false); + var labInfoResponse = await GetLabResponseAsync(InternalConstants.LabInfoEndpoint + user.LabName).ConfigureAwait(false); Lab[] labs = JsonConvert.DeserializeObject(labInfoResponse); user.TenantId = labs[0].TenantId; @@ -78,67 +88,67 @@ private Task RunQueryAsync(UserQuery query) //Required parameters will be set to default if not supplied by the test code queryDict.Add( - LabApiConstants.MultiFactorAuthentication, + InternalConstants.MultiFactorAuthentication, query.MFA != null ? query.MFA.ToString() : MFA.None.ToString()); queryDict.Add( - LabApiConstants.ProtectionPolicy, + InternalConstants.ProtectionPolicy, query.ProtectionPolicy != null ? query.ProtectionPolicy.ToString() : ProtectionPolicy.None.ToString()); if (query.UserType != null) { - queryDict.Add(LabApiConstants.UserType, query.UserType.ToString()); + queryDict.Add(InternalConstants.UserType, query.UserType.ToString()); } if (query.HomeDomain != null) { - queryDict.Add(LabApiConstants.HomeDomain, query.HomeDomain.ToString()); + queryDict.Add(InternalConstants.HomeDomain, query.HomeDomain.ToString()); } if (query.HomeUPN != null) { - queryDict.Add(LabApiConstants.HomeUPN, query.HomeUPN.ToString()); + queryDict.Add(InternalConstants.HomeUPN, query.HomeUPN.ToString()); } if (query.B2CIdentityProvider != null) { - queryDict.Add(LabApiConstants.B2CProvider, query.B2CIdentityProvider.ToString()); + queryDict.Add(InternalConstants.B2CProvider, query.B2CIdentityProvider.ToString()); } if (query.FederationProvider != null) { - queryDict.Add(LabApiConstants.FederationProvider, query.FederationProvider.ToString()); + queryDict.Add(InternalConstants.FederationProvider, query.FederationProvider.ToString()); } if (query.AzureEnvironment != null) { - queryDict.Add(LabApiConstants.AzureEnvironment, query.AzureEnvironment.ToString()); + queryDict.Add(InternalConstants.AzureEnvironment, query.AzureEnvironment.ToString()); } if (query.SignInAudience != null) { - queryDict.Add(LabApiConstants.SignInAudience, query.SignInAudience.ToString()); + queryDict.Add(InternalConstants.SignInAudience, query.SignInAudience.ToString()); } if (query.AppPlatform != null) { - queryDict.Add(LabApiConstants.AppPlatform, query.AppPlatform.ToString()); + queryDict.Add(InternalConstants.AppPlatform, query.AppPlatform.ToString()); } if (query.PublicClient != null) { - queryDict.Add(LabApiConstants.PublicClient, query.PublicClient.ToString()); + queryDict.Add(InternalConstants.PublicClient, query.PublicClient.ToString()); } - return SendLabRequestAsync(LabApiConstants.LabEndPoint, queryDict); + return SendLabRequestAsync(InternalConstants.LabEndPoint, queryDict); } else { - return SendLabRequestAsync(LabApiConstants.LabEndPoint + "/" + query.Upn, queryDict); + return SendLabRequestAsync(InternalConstants.LabEndPoint + "/" + query.Upn, queryDict); } } @@ -159,7 +169,7 @@ private async Task SendLabRequestAsync(string requestUrl, Dictionary GetLabResponseAsync(string address) { if (_labApiAccessToken == null) - _labApiAccessToken = await LabAuthenticationHelper.GetAccessTokenForLabAPIAsync(_labAccessAppId).ConfigureAwait(false); + _labApiAccessToken = await LabAuthenticationHelper.GetAccessTokenForLabAPIAsync().ConfigureAwait(false); using (HttpClient httpClient = new HttpClient()) { @@ -168,27 +178,17 @@ internal async Task GetLabResponseAsync(string address) } } - public async Task GetUserSecretAsync(string lab) + internal async Task GetUserSecretAsync(string lab) { Dictionary queryDict = new Dictionary { { "secret", lab } }; - string result = await SendLabRequestAsync(LabApiConstants.LabUserCredentialEndpoint, queryDict).ConfigureAwait(false); + string result = await SendLabRequestAsync(InternalConstants.LabUserCredentialEndpoint, queryDict).ConfigureAwait(false); return JsonConvert.DeserializeObject(result).Secret; } - public async Task GetMSIHelperServiceTokenAsync() - { - if (_msiHelperApiAccessToken == null) - { - _msiHelperApiAccessToken = await LabAuthenticationHelper - .GetAccessTokenForLabAPIAsync(_labAccessAppId) - .ConfigureAwait(false); - } - - return _msiHelperApiAccessToken.Value.Token; - } + } } diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceParameters.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceParameters.cs index c539d11223..428e993ebc 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceParameters.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabServiceParameters.cs @@ -44,14 +44,14 @@ public enum UserType MSA, } - public enum MFA + internal enum MFA { None, MfaOnAll, AutoMfaOnAll } - public enum ProtectionPolicy + internal enum ProtectionPolicy { None, CA, @@ -63,7 +63,7 @@ public enum ProtectionPolicy MAMSPO } - public enum HomeDomain //Must add ".com" to end for lab query + internal enum HomeDomain //Must add ".com" to end for lab query { None, MsidLab2, @@ -71,7 +71,7 @@ public enum ProtectionPolicy MsidLab4 } - public enum HomeUPN //Must replace "_" with "@" add ".com" to end for lab query + internal enum HomeUPN //Must replace "_" with "@" add ".com" to end for lab query { None, GidLab_Msidlab2, @@ -89,20 +89,20 @@ public enum AzureEnvironment azureusgovernment } - public enum SignInAudience + internal enum SignInAudience { AzureAdMyOrg, AzureAdMultipleOrgs, AzureAdAndPersonalMicrosoftAccount } - public enum AppPlatform + internal enum AppPlatform { web, spa } - public enum PublicClient + internal enum PublicClient { yes, no diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabUser.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabUser.cs index c3b09f5e40..34943c3b1f 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabUser.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabUser.cs @@ -22,13 +22,13 @@ public class LabUser public string DisplayName { get; set; } [JsonProperty("mfa")] - public MFA Mfa { get; set; } + internal MFA Mfa { get; set; } [JsonProperty("protectionpolicy")] - public ProtectionPolicy ProtectionPolicy { get; set; } + internal ProtectionPolicy ProtectionPolicy { get; set; } [JsonProperty("homedomain")] - public HomeDomain HomeDomain { get; set; } + internal HomeDomain HomeDomain { get; set; } [JsonProperty("homeupn")] public string HomeUPN { get; set; } diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserHelper.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserHelper.cs index 46017928c0..8af01235c4 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserHelper.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserHelper.cs @@ -1,31 +1,48 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; +using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Threading.Tasks; +using Azure; namespace Microsoft.Identity.Test.LabInfrastructure { public static class LabUserHelper { - private static readonly LabServiceApi s_labService; + private static readonly LabServiceApi s_labService = new LabServiceApi(); private static readonly ConcurrentDictionary s_userCache = new ConcurrentDictionary(); - public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsal { get; } - public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsid { get; } + public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsal { get; private set; } + public static KeyVaultSecretsProvider KeyVaultSecretsProviderMsid { get; private set; } + + private static bool _isInitialized = false; + private static readonly object _initializationLock = new object(); - static LabUserHelper() + public static void Initialize() { - KeyVaultSecretsProviderMsal = new KeyVaultSecretsProvider(KeyVaultInstance.MsalTeam); - KeyVaultSecretsProviderMsid = new KeyVaultSecretsProvider(KeyVaultInstance.MSIDLab); - s_labService = new LabServiceApi(); + if (_isInitialized) + { + return; + } + + lock (_initializationLock) + { + if (_isInitialized) + { + return; + } + + KeyVaultSecretsProviderMsal = new KeyVaultSecretsProvider(KeyVaultInstance.MsalTeam); + KeyVaultSecretsProviderMsid = new KeyVaultSecretsProvider(KeyVaultInstance.MSIDLab); + + _isInitialized = true; + } } - public static async Task GetLabUserDataAsync(UserQuery query) + internal static async Task GetLabUserDataAsync(UserQuery query) { + Initialize(); + if (s_userCache.ContainsKey(query)) { Trace.WriteLine("Lab user cache hit. Selected user: " + s_userCache[query].User.Upn); @@ -35,7 +52,7 @@ public static async Task GetLabUserDataAsync(UserQuery query) var response = await s_labService.GetLabResponseFromApiAsync(query).ConfigureAwait(false); if (response == null) { - throw new LabUserNotFoundException(query, "Found no users for the given query."); + throw new LabUserNotFoundException("Found no users for the given query: " + query); } bool added = s_userCache.TryAdd(query, response); @@ -53,23 +70,16 @@ public static Task GetLabUserDataForSpecificUserAsync(string upn) public static async Task GetMSIEnvironmentVariablesAsync(string uri) { + Initialize(); string result = await s_labService.GetLabResponseAsync(uri).ConfigureAwait(false); return result; } - /// - /// Returns the AAD cloud user idlab1@msidlab4.onmicrosoft.com - /// - /// public static Task GetDefaultUserAsync() { return GetLabUserDataAsync(UserQuery.PublicAadUserQuery); } - /// - /// Returns the AAD cloud user idlab@msidlab4.onmicrosoft.com - /// - /// public static Task GetDefaultUser2Async() { return GetLabUserDataAsync(UserQuery.PublicAadUser2Query); @@ -123,6 +133,17 @@ public static Task GetArlingtonUserAsync() return response; } + public static Task GetCiamUserAync() + { + var query = new UserQuery() + { + FederationProvider = FederationProvider.CIAMCUD, + SignInAudience = SignInAudience.AzureAdMyOrg + }; + + return GetLabUserDataAsync(query); + } + public static Task GetArlingtonADFSUserAsync() { var query = UserQuery.ArlingtonUserQuery; diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserNotFoundException.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserNotFoundException.cs index 07ecf2a875..577c46cf05 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserNotFoundException.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/LabUserNotFoundException.cs @@ -7,11 +7,15 @@ namespace Microsoft.Identity.Test.LabInfrastructure { public class LabUserNotFoundException : Exception { - public UserQuery Parameters { get; set; } + + public LabUserNotFoundException(string message) + : base(message) + { + } - public LabUserNotFoundException(UserQuery parameters, string message):base(message) + public LabUserNotFoundException(string message, Exception innerException) + : base(message, innerException) { - Parameters = parameters; } } } diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/Microsoft.Identity.Test.LabInfrastructure.csproj b/tests/Microsoft.Identity.Test.LabInfrastructure/Microsoft.Identity.Test.LabInfrastructure.csproj index 7c8ed47cba..9c01270ec9 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/Microsoft.Identity.Test.LabInfrastructure.csproj +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/Microsoft.Identity.Test.LabInfrastructure.csproj @@ -8,20 +8,13 @@ + + - - {3433eb33-114a-4db7-bc57-14f17f55da3c} - Microsoft.Identity.Client - - - - - - - Always - + + diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/UserQueryParameters.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/UserQueryParameters.cs index 3d11b80025..5cc176cf00 100644 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/UserQueryParameters.cs +++ b/tests/Microsoft.Identity.Test.LabInfrastructure/UserQueryParameters.cs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Collections.Generic; - namespace Microsoft.Identity.Test.LabInfrastructure { - public struct UserQuery + internal struct UserQuery { public UserType? UserType { get; set; } public MFA? MFA { get; set; } @@ -78,5 +75,10 @@ public struct UserQuery SignInAudience = LabInfrastructure.SignInAudience.AzureAdMyOrg }; + + public override string ToString() + { + return $"UserType: {UserType}, MFA: {MFA}, ProtectionPolicy: {ProtectionPolicy}, HomeDomain: {HomeDomain}, HomeUPN: {HomeUPN}, B2CIdentityProvider: {B2CIdentityProvider}, FederationProvider: {FederationProvider}, AzureEnvironment: {AzureEnvironment}, SignInAudience: {SignInAudience}, AppPlatform: {AppPlatform}, PublicClient: {PublicClient}, Upn: {Upn}"; + } } } diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/UserType.cs b/tests/Microsoft.Identity.Test.LabInfrastructure/UserType.cs deleted file mode 100644 index 86e69e2139..0000000000 --- a/tests/Microsoft.Identity.Test.LabInfrastructure/UserType.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Identity.Test.LabInfrastructure -{ - -} diff --git a/tests/Microsoft.Identity.Test.LabInfrastructure/data.txt b/tests/Microsoft.Identity.Test.LabInfrastructure/data.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/Microsoft.Identity.Test.Unit/Microsoft.Identity.Test.Unit.csproj b/tests/Microsoft.Identity.Test.Unit/Microsoft.Identity.Test.Unit.csproj index c5e90c1c03..c68730a538 100644 --- a/tests/Microsoft.Identity.Test.Unit/Microsoft.Identity.Test.Unit.csproj +++ b/tests/Microsoft.Identity.Test.Unit/Microsoft.Identity.Test.Unit.csproj @@ -70,6 +70,7 @@ + $(DefineConstants);NET_CORE