From 2693f86aaf43d7666fd055ab95872952f604a8a6 Mon Sep 17 00:00:00 2001 From: avdunn Date: Fri, 14 Nov 2025 15:03:07 -0800 Subject: [PATCH 01/14] Working proof-of-concept --- msal4j-sdk/pom.xml | 10 + .../ClientCredentialsIT.java | 57 ++-- .../UsernamePasswordIT.java | 140 ++++----- .../msal4j/labapi2/AppCredentialProvider.java | 28 ++ .../aad/msal4j/labapi2/AzureEnvironment.java | 10 + .../microsoft/aad/msal4j/labapi2/Config.java | 46 +++ .../labapi2/KeyVaultSecretsProvider.java | 161 ++++++++++ .../com/microsoft/aad/msal4j/labapi2/Lab.java | 80 +++++ .../aad/msal4j/labapi2/LabApiConstants.java | 41 +++ .../microsoft/aad/msal4j/labapi2/LabApp.java | 87 ++++++ .../msal4j/labapi2/LabCredentialResponse.java | 56 ++++ .../aad/msal4j/labapi2/LabHttpHelper.java | 87 ++++++ .../aad/msal4j/labapi2/LabResponse.java | 101 ++++++ .../aad/msal4j/labapi2/LabServiceApi.java | 262 ++++++++++++++++ .../msal4j/labapi2/LabServiceParameters.java | 32 ++ .../microsoft/aad/msal4j/labapi2/LabUser.java | 140 +++++++++ .../aad/msal4j/labapi2/LabUserHelper.java | 291 ++++++++++++++++++ .../labapi2/LabUserNotFoundException.java | 27 ++ .../aad/msal4j/labapi2/UserQuery.java | 67 ++++ 19 files changed, 1604 insertions(+), 119 deletions(-) create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index 15e4d751..35868b9f 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -129,6 +129,16 @@ 2.14.0 test + + com.azure + azure-security-keyvault-certificates + 4.5.0 + + + com.azure + azure-identity + 1.10.0 + diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java index 9cca7017..0bb91204 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java @@ -3,10 +3,8 @@ package com.microsoft.aad.msal4j; -import labapi.AppCredentialProvider; -import labapi.AzureEnvironment; -import labapi.LabUserProvider; -import labapi.User; +import com.microsoft.aad.msal4j.labapi2.KeyVaultSecretsProvider; +import com.microsoft.aad.msal4j.labapi2.LabServiceApi; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.BeforeAll; @@ -21,8 +19,6 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.Callable; import static com.microsoft.aad.msal4j.TestConstants.KEYVAULT_DEFAULT_SCOPE; @@ -30,12 +26,14 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ClientCredentialsIT { private IClientCertificate certificate; - private LabUserProvider labUserProvider; + private KeyVaultSecretsProvider keyVaultSecretsProvider; + private LabServiceApi labServiceApi; @BeforeAll void init() throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, IOException { certificate = CertificateHelper.getClientCertificate(); - labUserProvider = LabUserProvider.getInstance(); + keyVaultSecretsProvider = new KeyVaultSecretsProvider(); + labServiceApi = new LabServiceApi(); } @Test @@ -46,8 +44,7 @@ void acquireTokenClientCredentials_ClientCertificate() throws Exception { @Test void acquireTokenClientCredentials_ClientSecret() throws Exception { - AppCredentialProvider appProvider = new AppCredentialProvider(AzureEnvironment.AZURE); - final String clientId = appProvider.getLabVaultAppId(); + final String clientId = keyVaultSecretsProvider.getSecretByName("LabVaultAppID").getValue(); IClientCredential credential = CertificateHelper.getClientCertificate(); assertAcquireTokenCommon(clientId, credential, TestConstants.MICROSOFT_AUTHORITY); @@ -64,28 +61,21 @@ void acquireTokenClientCredentials_ClientAssertion() throws Exception { assertAcquireTokenCommon(clientId, credential, TestConstants.MICROSOFT_AUTHORITY); } - @Test - void acquireTokenClientCredentials_ClientSecret_Ciam() throws Exception { - - User user = labUserProvider.getCiamCudUser(); - String clientId = user.getAppId(); - - AppCredentialProvider appProvider = new AppCredentialProvider(AzureEnvironment.CIAM); - IClientCredential credential = ClientCredentialFactory.createFromSecret(appProvider.getOboAppPassword()); - - ConfidentialClientApplication cca = ConfidentialClientApplication.builder( - clientId, credential). - authority("https://" + user.getLabName() + ".ciamlogin.com/"). - build(); - - IAuthenticationResult result = cca.acquireToken(ClientCredentialParameters - .builder(Collections.singleton(TestConstants.DEFAULT_SCOPE)) - .build()) - .get(); - - assertNotNull(result); - assertNotNull(result.accessToken()); - } +// @Test +// void acquireTokenClientCredentials_ClientSecret_Ciam() throws Exception { +// User user = LabUserHelper.getCiamUserge(labServiceApi); +// String clientId = user.getAppId(); +// +// String ciamPassword = keyVaultSecretsProvider.getSecretByName("CiamAppPassword").getValu +// +// IAuthenticationResult result = cca.acquireToken(ClientCredentialParameters +// .builder(Collections.singleton(TestConstants.DEFAULT_SCOPE)) +// .build()) +// .get(); +// +// assertNotNull(result); +// assertNotNull(result.accessToken()); +// } @Test void acquireTokenClientCredentials_Certificate_CiamCud() throws Exception { @@ -132,8 +122,7 @@ void acquireTokenClientCredentials_Callback() throws Exception { @Test void acquireTokenClientCredentials_DefaultCacheLookup() throws Exception { - AppCredentialProvider appProvider = new AppCredentialProvider(AzureEnvironment.AZURE); - final String clientId = appProvider.getLabVaultAppId(); + final String clientId = keyVaultSecretsProvider.getSecretByName("LabVaultAppID").getValue(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder( clientId, CertificateHelper.getClientCertificate()). diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 20d99f57..074d0981 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -3,38 +3,28 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi2.Config; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.api.BeforeAll; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class UsernamePasswordIT { - private LabUserProvider labUserProvider; - private Config cfg; - @BeforeAll - void setUp() { - labUserProvider = LabUserProvider.getInstance(); - } - @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") void acquireTokenWithUsernamePassword_Managed(String environment) throws Exception { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); - - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), user.getAppId()); + LabResponse labResponse = LabUserHelper.getDefaultUserAsync(environment).join(); + assertAcquireTokenCommon(labResponse.getUser(), cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); } @ParameterizedTest @@ -43,76 +33,59 @@ void acquireTokenWithUsernamePassword_Managed(String environment) throws Excepti void acquireTokenWithUsernamePassword_ADFSv2019_Federated(String environment) throws Exception { cfg = new Config(environment); - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, cfg.azureEnvironment); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.ADFS_2019); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.FEDERATED); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUserAsync(environment).join(); + LabUser user = labResponse.getUser(); - User user = labUserProvider.getLabUser(query); - - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), user.getAppId()); + assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); } @Test @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void acquireTokenWithUsernamePassword_ADFSv2019_OnPrem() throws Exception { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.ADFS_2019); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.ON_PREM); - - User user = labUserProvider.getLabUser(query); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUserAsync().join(); + LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE, TestConstants.ADFS_APP_ID); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenWithUsernamePassword_ADFSv4(String environment) throws Exception { - cfg = new Config(environment); - - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, cfg.azureEnvironment); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.ADFS_4); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.FEDERATED); - - User user = labUserProvider.getLabUser(query); - - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), user.getAppId()); - } - - @Test - void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { - User user = labUserProvider.getDefaultUser(); - - assertAcquireTokenCommon( - user, - TestConstants.COMMON_AUTHORITY_WITH_PORT, - TestConstants.GRAPH_DEFAULT_SCOPE, - user.getAppId()); - } - - @Test - void acquireTokenWithUsernamePassword_Ciam() throws Exception { - Map extraQueryParameters = new HashMap<>(); - - User user = labUserProvider.getCiamCudUser(); - PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) - .authority("https://" + user.getLabName() + ".ciamlogin.com/") - .build(); - - IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(TestConstants.USER_READ_SCOPE), - user.getUpn(), - user.getPassword().toCharArray()) - .extraQueryParameters(extraQueryParameters) - .build()) - .get(); - - IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); - } - - private void assertAcquireTokenCommon(User user, String authority, String scope, String appId) +// @Test +// void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { +// LabResponse labResponse = LabUserHelper.getDefaultUserAsync().join(); +// LabUser user = labResponse.getUser(); +// +// assertAcquireTokenCommon( +// user, +// TestConstants.COMMON_AUTHORITY_WITH_PORT, +// TestConstants.GRAPH_DEFAULT_SCOPE, +// labResponse.getApp().getAppId()); +// } + +// @Test +// void acquireTokenWithUsernamePassword_Ciam() throws Exception { +// Map extraQueryParameters = new HashMap<>(); +// +// UserQuery query = new UserQuery(); +// query.setUserType(LabServiceParameters.UserType.CLOUD); +// query.setAzureEnvironment(LabServiceParameters.AzureEnvironment.AZURE_CIAM); +// +// LabResponse labResponse = LabUserHelper.getLabUserDataAsync(query).join(); +// +// LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) +// .authority("https://" + user.getLabName() + ".ciamlogin.com/") +// .build(); +// +// IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. +// builder(Collections.singleton(TestConstants.USER_READ_SCOPE), +// user.getUpn(), +// user.getPassword().toCharArray()) +// .extraQueryParameters(extraQueryParameters) +// .build()) +// .get(); +// +// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); +// } + + private void assertAcquireTokenCommon(LabUser user, String authority, String scope, String appId) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( @@ -120,12 +93,13 @@ private void assertAcquireTokenCommon(User user, String authority, String scope, authority(authority). build(); + System.out.println("Scope: " + scope); + System.out.println("UPN: " + user.getUpn()); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. builder(Collections.singleton(scope), user.getUpn(), user.getPassword().toCharArray()) .build()) - .get(); IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); @@ -134,13 +108,11 @@ private void assertAcquireTokenCommon(User user, String authority, String scope, @Test void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Exception { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.B2C); - query.parameters.put(UserQueryParameters.B2C_PROVIDER, B2CProvider.LOCAL); - User user = labUserProvider.getLabUser(query); + LabResponse labResponse = LabUserHelper.getB2CLocalAccountAsync().join(); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). b2cAuthority(TestConstants.B2C_AUTHORITY_ROPC). build(); @@ -166,13 +138,11 @@ void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Exception { @Test void acquireTokenWithUsernamePassword_B2C_LoginMicrosoftOnline() throws Exception { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.B2C); - query.parameters.put(UserQueryParameters.B2C_PROVIDER, B2CProvider.LOCAL); - User user = labUserProvider.getLabUser(query); + LabResponse labResponse = LabUserHelper.getB2CLocalAccountAsync().join(); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). b2cAuthority(TestConstants.B2C_MICROSOFTLOGIN_ROPC). build(); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java new file mode 100644 index 00000000..ef8dd4ec --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +public class AppCredentialProvider { + + private String clientId; + + public AppCredentialProvider(String azureEnvironment) { + + switch (azureEnvironment) { + case AzureEnvironment.AZURE: + clientId = "c0485386-1e9a-4663-bc96-7ab30656de7f"; + + break; + case AzureEnvironment.AZURE_US_GOVERNMENT: + clientId = LabApiConstants.ARLINGTON_APP_ID; + break; + default: + throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); + } + } + + public String getAppId() { + return clientId; + } +} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java new file mode 100644 index 00000000..2da771a0 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +public class AzureEnvironment { + + public static final String AZURE = "azurecloud"; + public static final String AZURE_US_GOVERNMENT = "azureusgovernment"; +} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java new file mode 100644 index 00000000..8536fe0c --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +public class Config { + private String organizationsAuthority; + private String graphDefaultScope; + AppCredentialProvider appProvider; + private String tenant; + + String azureEnvironment; + + public Config(String azureEnvironment) { + this.azureEnvironment = azureEnvironment; + + switch (azureEnvironment) { + case AzureEnvironment.AZURE: + organizationsAuthority = LabApiConstants.ORGANIZATIONS_AUTHORITY; + graphDefaultScope = LabApiConstants.GRAPH_DEFAULT_SCOPE; + appProvider = new AppCredentialProvider(azureEnvironment); + tenant = LabApiConstants.MICROSOFT_AUTHORITY_TENANT; + break; + case AzureEnvironment.AZURE_US_GOVERNMENT: + organizationsAuthority = LabApiConstants.ARLINGTON_ORGANIZATIONS_AUTHORITY; + graphDefaultScope = LabApiConstants.ARLINGTON_GRAPH_DEFAULT_SCOPE; + appProvider = new AppCredentialProvider(azureEnvironment); + tenant = LabApiConstants.ARLINGTON_AUTHORITY_TENANT; + break; + default: + throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); + } + } + + public String organizationsAuthority() { + return this.organizationsAuthority; + } + + public String graphDefaultScope() { + return this.graphDefaultScope; + } + + public String tenant() { + return this.tenant; + } +} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java new file mode 100644 index 00000000..db6b9fcd --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.SecretClientBuilder; +import com.azure.security.keyvault.secrets.models.KeyVaultSecret; +import com.microsoft.aad.msal4j.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.cert.X509Certificate; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import reactor.core.publisher.Mono; + +public class KeyVaultSecretsProvider implements AutoCloseable { + + private static final Logger log = LoggerFactory.getLogger(KeyVaultSecretsProvider.class); + + public static class KeyVaultInstance { + /** + * The KeyVault maintained by the MSID. It is recommended for use. + */ + public static final String MSID_LAB = "https://msidlabs.vault.azure.net"; + + /** + * The KeyVault maintained by the MSAL.NET team and have full control over. + * Should be used temporarily - secrets should be stored and managed by MSID Lab. + */ + public static final String MSAL_TEAM = "https://id4skeyvault.vault.azure.net/"; + } + + private final SecretClient secretClient; + + /** + * Initialize the secrets provider with the specified Key Vault address. + * + * Authentication using client certificate: + * 1. Register Azure AD application of "Web app / API" type. + * To set up certificate based access to the application PowerShell should be used. + * 2. Add an access policy entry to target Key Vault instance for this application. + * + * @param keyVaultAddress The Key Vault URI (defaults to MSID_LAB) + */ + public KeyVaultSecretsProvider(String keyVaultAddress) { + String vaultUrl = keyVaultAddress != null ? keyVaultAddress : KeyVaultInstance.MSID_LAB; + log.debug("Initializing KeyVault secrets provider for: {}", vaultUrl); + + TokenCredential credentials = getKeyVaultCredential(); + + this.secretClient = new SecretClientBuilder() + .vaultUrl(vaultUrl) + .credential(credentials) + .buildClient(); + + log.debug("KeyVault secrets provider initialized successfully"); + } + + public KeyVaultSecretsProvider() { + this(KeyVaultInstance.MSID_LAB); + } + + /** + * Get a secret by name from Key Vault. + * + * @param secretName The name of the secret + * @return The KeyVaultSecret object + */ + public KeyVaultSecret getSecretByName(String secretName) { + log.debug("Retrieving secret from Key Vault: {}", secretName); + try { + KeyVaultSecret secret = secretClient.getSecret(secretName); + log.debug("Successfully retrieved secret: {}", secretName); + return secret; + } catch (Exception e) { + log.error("Failed to retrieve secret '{}': {}", secretName, e.getMessage()); + throw e; + } + } + + /** + * Get credentials for accessing Key Vault. + * Uses LabAuthenticationHelper to obtain an access token. + * + * @return TokenCredential for Key Vault access + */ + private TokenCredential getKeyVaultCredential() { + return tokenRequestContext -> Mono.defer(() -> Mono.just(requestAccessTokenForAutomation())); + } + + private AccessToken requestAccessTokenForAutomation() { + IAuthenticationResult result; + try { + log.debug("Acquiring access token for Key Vault"); + ConfidentialClientApplication cca = ConfidentialClientApplication.builder( + TestConstants.MSIDLAB_CLIENT_ID, + getClientCredentialFromKeyStore()) + .authority(TestConstants.MICROSOFT_AUTHORITY) + .sendX5c(true) + .build(); + + result = cca.acquireToken(ClientCredentialParameters + .builder(Collections.singleton(TestConstants.KEYVAULT_DEFAULT_SCOPE)) + .build()) + .get(); + + log.debug("Successfully acquired Key Vault access token"); + } catch (Exception e) { + log.error("Error acquiring token from Azure AD: {}", e.getMessage(), e); + throw new RuntimeException("Error acquiring token from Azure AD: " + e.getMessage()); + } + + if (result != null) { + return new AccessToken( + result.accessToken(), + OffsetDateTime.ofInstant(result.expiresOnDate().toInstant(), ZoneOffset.UTC)); + } else { + log.error("Authentication result is null"); + throw new NullPointerException("Authentication result is null"); + } + } + + IClientCredential getClientCredentialFromKeyStore() { + PrivateKey key; + X509Certificate publicCertificate; + try { + log.debug("Loading client certificate from keystore"); + String os = System.getProperty("os.name"); + KeyStore keystore; + if (os.toLowerCase().contains("windows")) { + log.debug("Using Windows-MY keystore"); + keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI"); + } else { + log.debug("Using KeychainStore keystore"); + keystore = KeyStore.getInstance("KeychainStore"); + } + + keystore.load(null, null); + key = (PrivateKey) keystore.getKey("LabAuth.MSIDLab.com", null); + publicCertificate = (X509Certificate) keystore.getCertificate("LabAuth.MSIDLab.com"); + + log.debug("Successfully loaded client certificate from keystore"); + } catch (Exception e) { + log.error("Error getting certificate from keystore: {}", e.getMessage(), e); + throw new RuntimeException("Error getting certificate from keystore: " + e.getMessage()); + } + return ClientCredentialFactory.createFromCertificate(key, publicCertificate); + } + + @Override + public void close() { + // Cleanup if needed + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java new file mode 100644 index 00000000..2f2448de --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class Lab implements JsonSerializable { + private String labName; + private String domain; + private String tenantId; + private String federationProvider; + private String azureEnvironment; + private String authority; + + static Lab fromJson(JsonReader jsonReader) throws IOException { + Lab lab = new Lab(); + + return jsonReader.readObject(reader -> { + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + switch (fieldName) { + case "labName": + lab.labName = reader.getString(); + break; + case "domain": + lab.domain = reader.getString(); + break; + case "tenantId": + lab.tenantId = reader.getString(); + break; + case "federationProvider": + lab.federationProvider = reader.getString(); + break; + case "azureEnvironment": + lab.azureEnvironment = reader.getString(); + break; + case "authority": + lab.authority = reader.getString(); + break; + default: + reader.skipChildren(); + break; + } + } + return lab; + }); + } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + + jsonWriter.writeStringField("labName", labName); + jsonWriter.writeStringField("domain", domain); + jsonWriter.writeStringField("tenantId", tenantId); + jsonWriter.writeStringField("federationProvider", federationProvider); + jsonWriter.writeStringField("azureEnvironment", azureEnvironment); + jsonWriter.writeStringField("authority", authority); + + jsonWriter.writeEndObject(); + + return jsonWriter; + } + + public String getTenantId() { + return this.tenantId; + } + + public String getFederationProvider() { + return this.federationProvider; + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java new file mode 100644 index 00000000..aa0bb85a --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +/** + * Constants for lab API endpoints and query parameters. + */ +public class LabApiConstants { + + // Base endpoints + public static final String LAB_ENDPOINT = "https://msidlab.com/api/user"; + public static final String LAB_USER_CREDENTIAL_ENDPOINT = "https://msidlab.com/api/LabSecret"; + public static final String LAB_APP_ENDPOINT = "https://msidlab.com/api/app/"; + public static final String LAB_INFO_ENDPOINT = "https://msidlab.com/api/Lab/"; + + // Query parameter keys + public static final String USER_TYPE = "usertype"; + public static final String MULTI_FACTOR_AUTHENTICATION = "mfa"; + public static final String PROTECTION_POLICY = "protectionpolicy"; + public static final String HOME_DOMAIN = "homedomain"; + public static final String B2C_PROVIDER = "b2cprovider"; + public static final String FEDERATION_PROVIDER = "federationprovider"; + public static final String AZURE_ENVIRONMENT = "azureenvironment"; + + public static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; + public static final String ARLINGTON_MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.us/"; + public static final String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; + public static final String ARLINGTON_AUTHORITY_TENANT = "arlmsidlab1.onmicrosoft.us"; + + public static final String ORGANIZATIONS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "organizations/"; + public static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; + + public static final String ARLINGTON_ORGANIZATIONS_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "organizations/"; + public static final String ARLINGTON_GRAPH_DEFAULT_SCOPE = "https://graph.microsoft.us/.default"; + public static final String GRAPH_DEFAULT_SCOPE = "https://graph.windows.net/.default"; + + public static final String ARLINGTON_APP_ID = "cb7faed4-b8c0-49ee-b421-f5ed16894c83"; + public static final String LAB_API_SCOPE = "https://request.msidlab.com/.default"; + +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java new file mode 100644 index 00000000..a650a4e9 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class LabApp implements JsonSerializable { + + private String appType; + private String appName; + private String appId; + private String redirectUri; + private String authority; + private String labName; + private String clientSecret; + + static LabApp fromJson(JsonReader jsonReader) throws IOException { + LabApp app = new LabApp(); + + return jsonReader.readObject(reader -> { + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + switch (fieldName) { + case "appType": + app.appType = reader.getString(); + break; + case "appName": + app.appName = reader.getString(); + break; + case "appId": + app.appId = reader.getString(); + break; + case "redirectUri": + app.redirectUri = reader.getString(); + break; + case "authority": + app.authority = reader.getString(); + break; + case "labName": + app.labName = reader.getString(); + break; + case "clientSecret": + app.clientSecret = reader.getString(); + break; + default: + reader.skipChildren(); + break; + } + } + return app; + }); + } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + + jsonWriter.writeStringField("appType", appType); + jsonWriter.writeStringField("appName", appName); + jsonWriter.writeStringField("appId", appId); + jsonWriter.writeStringField("redirectUri", redirectUri); + jsonWriter.writeStringField("authority", authority); + jsonWriter.writeStringField("labName", labName); + jsonWriter.writeStringField("clientSecret", clientSecret); + + jsonWriter.writeEndObject(); + + return jsonWriter; + } + + public String getAuthority() { + return authority; + } + + public String getAppId() { + return appId; + } + +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java new file mode 100644 index 00000000..dc80ac15 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +/** + * Response object for user credential requests from lab API. + */ +public class LabCredentialResponse implements JsonSerializable { + + private String secret; + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + /** + * Deserialize a LabCredentialResponse from JSON. + */ + public static LabCredentialResponse fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + LabCredentialResponse response = new LabCredentialResponse(); + + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + if ("secret".equals(fieldName)) { + response.secret = reader.getString(); + } else { + reader.skipChildren(); + } + } + return response; + }); + } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + jsonWriter.writeStringField("secret", secret); + jsonWriter.writeEndObject(); + return jsonWriter; + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java new file mode 100644 index 00000000..b4cdc569 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java @@ -0,0 +1,87 @@ +package com.microsoft.aad.msal4j.labapi2; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.Map; + +class LabHttpHelper { + + private static final Logger log = LoggerFactory.getLogger(LabHttpHelper.class); + + + static String sendRequestToLab(String url, Map queryMap, String accessToken) throws IOException { + return sendRequestToLab(buildUrl(url, queryMap), accessToken); + } + + static String sendRequestToLab(String url, String accessToken) throws IOException { + return sendRequestToLab(new URL(url), accessToken); + } + + static String sendRequestToLab(URL labUrl, String accessToken) throws IOException { + log.debug("Sending HTTP GET request to: {}", labUrl); + + HttpsURLConnection conn = (HttpsURLConnection) labUrl.openConnection(); + + conn.setRequestProperty("Authorization", "Bearer " + accessToken); + conn.setRequestProperty("Accept", "application/json"); + conn.setReadTimeout(30000); // 30 seconds + conn.setConnectTimeout(30000); + + int responseCode = conn.getResponseCode(); + log.debug("HTTP response code: {}", responseCode); + + StringBuilder content = new StringBuilder(); + try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + } catch (IOException e) { + log.error("Failed to read response from {}: {}", labUrl, e.getMessage()); + throw e; + } finally { + conn.disconnect(); + } + + String response = content.toString(); + log.debug("HTTP response received: {} characters", response.length()); + + return response; + } + + static URL buildUrl(String url, Map queryMap) throws MalformedURLException { + if (queryMap.isEmpty()) { + return new URL(url); + } + + StringBuilder queryParameters = new StringBuilder(); + for (Map.Entry entry : queryMap.entrySet()) { + if (queryParameters.length() > 0) { + queryParameters.append("&"); + } + queryParameters.append(encodeUTF8(entry.getKey())) + .append("=") + .append(encodeUTF8(entry.getValue())); + } + + String urlString = url + "?" + queryParameters; + return new URL(urlString); + } + + private static String encodeUTF8(String s) { + try { + return URLEncoder.encode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Error: cannot encode query parameter " + s); + } + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java new file mode 100644 index 00000000..dd6392e7 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +/** + * Container for lab API response data containing user, app, and lab information. + */ +public class LabResponse implements JsonSerializable { + + private LabUser user; + private LabApp app; + private Lab lab; + + public LabUser getUser() { + return user; + } + + public void setUser(LabUser user) { + this.user = user; + } + + public LabApp getApp() { + return app; + } + + public void setApp(LabApp app) { + this.app = app; + } + + public Lab getLab() { + return lab; + } + + public void setLab(Lab lab) { + this.lab = lab; + } + + /** + * Deserialize a LabResponse from JSON. + */ + public static LabResponse fromJson(JsonReader jsonReader) throws IOException { + return jsonReader.readObject(reader -> { + LabResponse response = new LabResponse(); + + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + switch (fieldName) { + case "user": + response.user = LabUser.fromJson(reader); + break; + case "app": + response.app = LabApp.fromJson(reader); + break; + case "lab": + response.lab = Lab.fromJson(reader); + break; + default: + reader.skipChildren(); + break; + } + } + return response; + }); + } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + + if (user != null) { + jsonWriter.writeJsonField("user", user); + } + if (app != null) { + jsonWriter.writeJsonField("app", app); + } + if (lab != null) { + jsonWriter.writeJsonField("lab", lab); + } + + jsonWriter.writeEndObject(); + return jsonWriter; + } + + @Override + public String toString() { + return String.format("LabResponse{user=%s, app=%s, lab=%s}", + user != null ? user.getUpn() : "null", + app != null ? app.getAppId() : "null", + lab != null ? lab.getTenantId() : "null"); + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java new file mode 100644 index 00000000..31c70623 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java @@ -0,0 +1,262 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonProviders; +import com.azure.json.JsonReader; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.MFA; +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.ProtectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static com.microsoft.aad.msal4j.labapi2.LabHttpHelper.buildUrl; + +/** + * Wrapper for lab service API interactions. + */ +public class LabServiceApi { + + private static final Logger log = LoggerFactory.getLogger(LabServiceApi.class); + + private ConfidentialClientApplication labApp; + + + /** + * Returns a test user account for use in testing. + * + * @param query Any and all parameters that the returned user should satisfy + * @return CompletableFuture containing LabResponse with user that matches the query + */ + public CompletableFuture getLabResponseFromApiAsync(UserQuery query) { + log.debug("Querying lab API for user with parameters: {}", query); + + return runQueryAsync(query) + .thenCompose(result -> { + if (result == null || result.trim().isEmpty()) { + log.error("No lab user found for query"); + throw new LabUserNotFoundException(query, + "No lab user with specified parameters exists"); + } + log.debug("Lab API returned {} characters of data", result.length()); + return createLabResponseFromResultStringAsync(result); + }) + .whenComplete((response, error) -> { + if (error != null) { + log.error("Failed to get lab response: {}", error.getMessage()); + } else if (response != null && response.getUser() != null) { + log.info("Successfully retrieved lab user: {}", response.getUser().getUpn()); + } + }); + } + + /** + * Create a LabResponse object from JSON result string. + * + * @param result JSON string containing lab user array + * @return CompletableFuture containing populated LabResponse + */ + CompletableFuture createLabResponseFromResultStringAsync(String result) { + return CompletableFuture.supplyAsync(() -> { + try { + log.debug("Parsing lab user JSON response"); + + List userResponses; + try (JsonReader jsonReader = JsonProviders.createReader(result)) { + userResponses = jsonReader.readArray(LabUser::fromJson); + } + + if (userResponses.isEmpty()) { + log.error("Lab API returned empty user array"); + throw new RuntimeException("Lab API returned empty user array"); + } + + LabUser user = userResponses.get(0); + log.debug("Parsed lab user: {}, fetching app and lab info", user.getUpn()); + + // Fetch app and lab info + String appEndpoint = LabApiConstants.LAB_APP_ENDPOINT + user.getAppId(); + log.debug("Fetching app info from: {}", appEndpoint); + String appResponse = getLabResponseAsync(appEndpoint).join(); + + List labApps; + try (JsonReader appReader = JsonProviders.createReader(appResponse)) { + labApps = appReader.readArray(LabApp::fromJson); + } + + if (labApps.isEmpty()) { + log.error("No lab app found for appId: {}", user.getAppId()); + throw new RuntimeException("No lab app found for appId: " + user.getAppId()); + } + log.debug("Successfully parsed lab app: {}", labApps.get(0).getAppId()); + + String labInfoEndpoint = LabApiConstants.LAB_INFO_ENDPOINT + user.getLabName(); + log.debug("Fetching lab info from: {}", labInfoEndpoint); + String labInfoResponse = getLabResponseAsync(labInfoEndpoint).join(); + + List labs; + try (JsonReader labReader = JsonProviders.createReader(labInfoResponse)) { + labs = labReader.readArray(Lab::fromJson); + } + + if (labs.isEmpty()) { + log.error("No lab info found for labName: {}", user.getLabName()); + throw new RuntimeException("No lab info found for labName: " + user.getLabName()); + } + log.debug("Successfully parsed lab info for: {}", user.getLabName()); + + LabResponse response = new LabResponse(); + response.setUser(user); + response.setApp(labApps.get(0)); + response.setLab(labs.get(0)); + + log.info("Successfully created LabResponse for user: {}", user.getUpn()); + return response; + + } catch (Exception e) { + log.error("Failed to create LabResponse from API result: {}", e.getMessage(), e); + throw new RuntimeException("Failed to create LabResponse from API result", e); + } + }); + } + + /** + * Execute a query against the lab API. + * + * @param query UserQuery parameters + * @return CompletableFuture containing JSON response string + */ + private CompletableFuture runQueryAsync(UserQuery query) { + Map queryDict = new HashMap<>(); + + // Building user query - required parameters set to defaults if not supplied + queryDict.put( + LabApiConstants.MULTI_FACTOR_AUTHENTICATION, + MFA.NONE.toString() + ); + + queryDict.put( + LabApiConstants.PROTECTION_POLICY, + ProtectionPolicy.NONE.toString() + ); + + if (query.getUserType() != null) { + queryDict.put(LabApiConstants.USER_TYPE, query.getUserType().toString()); + } + + if (query.getB2cIdentityProvider() != null) { + queryDict.put(LabApiConstants.B2C_PROVIDER, + query.getB2cIdentityProvider().toString()); + } + + if (query.getFederationProvider() != null) { + queryDict.put(LabApiConstants.FEDERATION_PROVIDER, + query.getFederationProvider().toString()); + } + + if (query.getAzureEnvironment() != null) { + queryDict.put(LabApiConstants.AZURE_ENVIRONMENT, + query.getAzureEnvironment().toString()); + } + + return sendLabRequestAsync(LabApiConstants.LAB_ENDPOINT, queryDict); + } + + /** + * Send HTTP request to lab API with query parameters. + * + * @param requestUrl Base URL for the request + * @param queryDict Query parameters + * @return CompletableFuture containing response string + */ + private CompletableFuture sendLabRequestAsync(String requestUrl, Map queryDict) { + return CompletableFuture.supplyAsync(() -> { + try { + log.debug("Acquiring lab access token"); + String accessToken = getLabAccessToken(); + log.debug("Sending lab request to: {} with {} query parameters", requestUrl, queryDict.size()); + + String response = LabHttpHelper.sendRequestToLab(requestUrl, queryDict, accessToken); + log.debug("Lab request successful, received {} characters", response.length()); + return response; + } catch (IOException e) { + log.error("Failed to send lab request to {}: {}", requestUrl, e.getMessage()); + throw new RuntimeException("Failed to send lab request", e); + } + }); + } + + private void initLabApp() { + if (labApp == null) { + log.debug("Initializing lab app"); + KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); + + String appID = keyVaultSecretsProvider.getSecretByName("LabVaultAppID").getValue(); + log.debug("Retrieved lab app ID from Key Vault"); + + try { + labApp = ConfidentialClientApplication.builder( + appID, + keyVaultSecretsProvider.getClientCredentialFromKeyStore()) + .authority(LabApiConstants.MICROSOFT_AUTHORITY) + .build(); + log.debug("Lab app initialized successfully"); + } catch (Exception e) { + log.error("Error initializing lab app: {}", e.getMessage(), e); + throw new RuntimeException("Error initializing lab app: " + e.getMessage()); + } + } + } + + private String getLabAccessToken() { + try { + initLabApp(); + + log.debug("Acquiring lab access token"); + IAuthenticationResult result = labApp.acquireToken( + ClientCredentialParameters + .builder(Collections.singleton(LabApiConstants.LAB_API_SCOPE)) + .build() + ).get(); + + log.debug("Successfully acquired lab access token"); + return result.accessToken(); + + } catch (Exception e) { + log.error("Error acquiring lab access token: {}", e.getMessage(), e); + throw new RuntimeException("Error acquiring lab access token: " + e.getMessage()); + } + } + + /** + * Execute HTTP GET request to lab API endpoint. + * + * @param address Full URL to request + * @return CompletableFuture containing response body as string + */ + CompletableFuture getLabResponseAsync(String address) { + return CompletableFuture.supplyAsync(() -> { + try { + log.debug("Getting lab response from: {}", address); + String accessToken = getLabAccessToken(); + String response = LabHttpHelper.sendRequestToLab(address, accessToken); + log.debug("Successfully retrieved lab response: {} characters", response.length()); + return response; + } catch (IOException e) { + log.error("Failed to get lab response from {}: {}", address, e.getMessage()); + throw new RuntimeException("Failed to get lab response from: " + address, e); + } + }); + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java new file mode 100644 index 00000000..f1b2fc63 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +public class LabServiceParameters { + + public enum FederationProvider { + ADFS_V4, + @Deprecated // ADFSv3 is out of support, do not use. The Arlington lab is federated to ADFSv3, so this value is needed + ADFS_V3, + ADFS_V2019, + } + + public enum B2CIdentityProvider { + LOCAL, // Local B2C account + } + + public enum UserType { + B2C, + CLOUD, + FEDERATED, + } + + public enum MFA { + NONE, + } + + public enum ProtectionPolicy { + NONE, + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java new file mode 100644 index 00000000..beb8edfc --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java @@ -0,0 +1,140 @@ +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonReader; +import com.azure.json.JsonSerializable; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +public class LabUser implements JsonSerializable { + private String appId; + private String objectId; + private String userType; + private String displayName; + private String licenses; + private String upn; + private String mfa; + private String protectionPolicy; + private String homeDomain; + private String homeUPN; + private String b2cProvider; + private String labName; + private String lastUpdatedBy; + private String lastUpdatedDate; + private String tenantId; + private String password; + + static LabUser fromJson(JsonReader jsonReader) throws IOException { + LabUser user = new LabUser(); + + return jsonReader.readObject(reader -> { + while (reader.nextToken() != JsonToken.END_OBJECT) { + String fieldName = reader.getFieldName(); + reader.nextToken(); + + switch (fieldName) { + case "appId": + user.appId = reader.getString(); + break; + case "objectId": + user.objectId = reader.getString(); + break; + case "userType": + user.userType = reader.getString(); + break; + case "displayName": + user.displayName = reader.getString(); + break; + case "licenses": + user.licenses = reader.getString(); + break; + case "upn": + user.upn = reader.getString(); + break; + case "mfa": + user.mfa = reader.getString(); + break; + case "protectionPolicy": + user.protectionPolicy = reader.getString(); + break; + case "homeDomain": + user.homeDomain = reader.getString(); + break; + case "homeUPN": + user.homeUPN = reader.getString(); + break; + case "b2cProvider": + user.b2cProvider = reader.getString(); + break; + case "labName": + user.labName = reader.getString(); + break; + case "lastUpdatedBy": + user.lastUpdatedBy = reader.getString(); + break; + case "lastUpdatedDate": + user.lastUpdatedDate = reader.getString(); + break; + case "tenantId": + user.tenantId = reader.getString(); + break; + default: + reader.skipChildren(); + break; + } + } + return user; + }); + } + + @Override + public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.writeStartObject(); + + jsonWriter.writeStringField("appId", appId); + jsonWriter.writeStringField("objectId", objectId); + jsonWriter.writeStringField("userType", userType); + jsonWriter.writeStringField("displayName", displayName); + jsonWriter.writeStringField("licenses", licenses); + jsonWriter.writeStringField("upn", upn); + jsonWriter.writeStringField("mfa", mfa); + jsonWriter.writeStringField("protectionPolicy", protectionPolicy); + jsonWriter.writeStringField("homeDomain", homeDomain); + jsonWriter.writeStringField("homeUPN", homeUPN); + jsonWriter.writeStringField("b2cProvider", b2cProvider); + jsonWriter.writeStringField("labName", labName); + jsonWriter.writeStringField("lastUpdatedBy", lastUpdatedBy); + jsonWriter.writeStringField("lastUpdatedDate", lastUpdatedDate); + jsonWriter.writeStringField("tenantId", tenantId); + + jsonWriter.writeEndObject(); + + return jsonWriter; + } + + public String getAppId() { + return this.appId; + } + + public String getUpn() { + return this.upn; + } + + public String getLabName() { + return this.labName; + } + + /** + * Get the user's password, fetching from MSID Key Vault if necessary. + * + * @return The user's password + */ + public String getPassword() { + if (password == null || password.isEmpty()) { + // Fetch from MSID Lab Key Vault + password = LabUserHelper.fetchUserPassword(labName); + } + return password; + } +} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java new file mode 100644 index 00000000..26f89d40 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -0,0 +1,291 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.azure.json.JsonProviders; +import com.azure.json.JsonReader; +import com.azure.security.keyvault.secrets.models.KeyVaultSecret; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +public class LabUserHelper { + + private static final Logger log = LoggerFactory.getLogger(LabUserHelper.class); + private static final LabServiceApi labService = new LabServiceApi(); + private static final ConcurrentHashMap userCache = + new ConcurrentHashMap<>(); + + private static final KeyVaultSecretsProvider keyVaultSecretsProviderMsal; + private static final KeyVaultSecretsProvider keyVaultSecretsProviderMsid; + + static { + // MSAL Team vault - for configuration data + keyVaultSecretsProviderMsal = new KeyVaultSecretsProvider( + KeyVaultSecretsProvider.KeyVaultInstance.MSAL_TEAM); + + // MSID Lab vault - for user passwords + keyVaultSecretsProviderMsid = new KeyVaultSecretsProvider( + KeyVaultSecretsProvider.KeyVaultInstance.MSID_LAB); + } + + /** + * Get lab user data with caching support. + * + * @param query The UserQuery to search for + * @return CompletableFuture containing LabResponse + */ + public static CompletableFuture getLabUserDataAsync(UserQuery query) { + if (userCache.containsKey(query)) { + LabResponse cached = userCache.get(query); + log.debug("Lab cache hit: {}", + cached.getUser() != null ? cached.getUser().getUpn() : "N/A"); + return CompletableFuture.completedFuture(cached); + } + + return labService.getLabResponseFromApiAsync(query) + .thenApply(response -> { + if (response == null) { + log.error("No lab user found for query"); + throw new LabUserNotFoundException(query, "Found no users for the given query."); + } + + log.info("Lab API returned user: {}", + response.getUser() != null ? response.getUser().getUpn() : "N/A"); + + userCache.put(query, response); + return response; + }); + } + + /** + * Get lab data from Key Vault by secret name. + * Uses the MSAL Team vault for configuration data. + * + * @param secret The Key Vault secret name + * @return CompletableFuture containing either LabResponse or String + */ + public static CompletableFuture getKVLabData(String secret) { + return CompletableFuture.supplyAsync(() -> { + try { + log.debug("Retrieving Key Vault secret: {}", secret); + // Use MSAL Team vault for configuration + KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsal.getSecretByName(secret); + String labData = keyVaultSecret.getValue(); + + if (labData == null || labData.isEmpty()) { + log.error("Key Vault secret '{}' is empty", secret); + throw new LabUserNotFoundException(new UserQuery(), + "Found no content for secret '" + secret + "' in Key Vault."); + } + + // Check if the value is JSON + if (isValidJson(labData)) { + LabResponse response; + try (JsonReader jsonReader = JsonProviders.createReader(labData)) { + response = LabResponse.fromJson(jsonReader); + } + + if (response == null) { + log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secret); + throw new LabUserNotFoundException(new UserQuery(), + "Failed to deserialize Key Vault secret '" + secret + "' to LabResponse."); + } + + log.debug("Retrieved LabResponse from Key Vault '{}': {}", secret, + response.getUser() != null ? response.getUser().getUpn() : + response.getApp() != null ? response.getApp().getAppId() : "Unknown"); + return response; + } else { + log.debug("Retrieved raw string from Key Vault '{}': {} characters", secret, labData.length()); + return labData; + } + } catch (Exception e) { + log.error("Failed to retrieve Key Vault secret '{}': {}", secret, e.getMessage()); + throw new RuntimeException( + "Failed to retrieve or parse Key Vault secret '" + secret + "'", e); + } + }); + } + + /** + * Fetch user password from Key Vault. + * Uses the MSID Lab vault (different from configuration vault). + * + * @param userLabName The lab name of the user (used as secret name) + * @return The user's password + */ + public static String fetchUserPassword(String userLabName) { + if (userLabName == null || userLabName.trim().isEmpty()) { + log.error("Password fetch failed: empty lab name"); + throw new IllegalArgumentException( + "Error: lab name is not set on user. Password retrieval failed."); + } + + if (keyVaultSecretsProviderMsid == null || keyVaultSecretsProviderMsal == null) { + log.error("Password fetch failed: KeyVault provider not initialized"); + throw new IllegalStateException("Error: KeyVault secrets provider is not set"); + } + + try { + log.debug("Fetching user password from MSID Lab Key Vault for: {}", userLabName); + // Use MSID vault for passwords, not MSAL vault + KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsid.getSecretByName(userLabName); + String password = keyVaultSecret.getValue(); + + if (password != null && !password.isEmpty()) { + log.debug("Password retrieved for user: {} ({} characters)", userLabName, password.length()); + return password; + } + + log.error("Password empty for user: {}", userLabName); + throw new IllegalStateException( + "Password secret '" + userLabName + "' found but was empty in Key Vault."); + } catch (Exception e) { + log.error("Password fetch failed for user {}: {}", userLabName, e.getMessage()); + throw new RuntimeException( + "Test setup: cannot get the user password from Key Vault secret '" + + userLabName + "'", e); + } + } + + /** + * Merge multiple Key Vault secrets into a single LabResponse. + * Each secret should contain a LabResponse JSON object. + * Fields from later secrets override fields from earlier ones. + * + * @param secrets Array of Key Vault secret names to merge + * @return Merged LabResponse + */ + public static LabResponse mergeKVLabData(String... secrets) { + if (secrets == null || secrets.length == 0) { + throw new IllegalArgumentException( + "At least one secret name must be provided."); + } + + try { + LabResponse mergedResponse = new LabResponse(); + boolean hasValidResponse = false; + + for (String secret : secrets) { + + Object data = getKVLabData(secret).join(); + + if (data instanceof LabResponse) { + LabResponse response = (LabResponse) data; + hasValidResponse = true; + + // Merge user, app, and lab fields (later values override earlier ones) + if (response.getUser() != null) { + mergedResponse.setUser(response.getUser()); + } + if (response.getApp() != null) { + mergedResponse.setApp(response.getApp()); + } + if (response.getLab() != null) { + mergedResponse.setLab(response.getLab()); + } + } + } + + if (!hasValidResponse) { + log.error("Failed to merge secrets - no valid LabResponse found: {}", + String.join(", ", secrets)); + throw new LabUserNotFoundException(new UserQuery(), + "Failed to create merged LabResponse from secrets: " + + String.join(", ", secrets)); + } + + log.info("Merged secrets [{}]: {}", String.join(", ", secrets), + mergedResponse.getUser() != null ? mergedResponse.getUser().getUpn() : "N/A"); + + return mergedResponse; + } catch (Exception e) { + log.error("Failed to merge secrets [{}]: {}", String.join(", ", secrets), e.getMessage()); + throw new RuntimeException( + "Failed to merge Key Vault secrets: " + String.join(", ", secrets), e); + } + } + + /** + * Check if a string is valid JSON. + */ + private static boolean isValidJson(String value) { + try (JsonReader jsonReader = JsonProviders.createReader(value)) { + jsonReader.nextToken(); + return true; + } catch (Exception e) { + return false; + } + } + + public static CompletableFuture getDefaultUserAsync() { + return CompletableFuture.completedFuture( + mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON")); + } + + public static CompletableFuture getDefaultAdfsUserAsync() { + return CompletableFuture.completedFuture( + mergeKVLabData("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON")); + } + + public static CompletableFuture getB2CLocalAccountAsync() { + return getLabUserDataAsync(UserQuery.b2cLocalAccountUserQuery()); + } + + public static CompletableFuture getArlingtonUserAsync() { + // Query the Lab API with Arlington-specific parameters + return getLabUserDataAsync(UserQuery.arlingtonUserQuery()); + } + + public static CompletableFuture getArlingtonADFSUserAsync() { + // Create a modified query with federated user type + UserQuery query = UserQuery.arlingtonUserQuery(); + query.setUserType(LabServiceParameters.UserType.FEDERATED); + + return getLabUserDataAsync(query); + } + + /** + * Get a default managed user for the specified Azure environment. + * This is the primary helper method for parameterized tests that run across multiple clouds. + * + * @param azureEnvironment The Azure environment (e.g., AzureEnvironment.AZURE or AZURE_US_GOVERNMENT) + * @return CompletableFuture containing LabResponse for a managed user in that environment + */ + public static CompletableFuture getDefaultUserAsync(String azureEnvironment) { + log.debug("Getting default user for environment: {}", azureEnvironment); + + if (AzureEnvironment.AZURE.equals(azureEnvironment)) { + return getDefaultUserAsync(); + } else if (AzureEnvironment.AZURE_US_GOVERNMENT.equals(azureEnvironment)) { + return getArlingtonUserAsync(); + } else { + log.error("Unsupported Azure environment: {}", azureEnvironment); + throw new IllegalArgumentException("Unsupported Azure environment: " + azureEnvironment); + } + } + + /** + * Get a default ADFS user for the specified Azure environment. + * Currently ADFS users are environment-agnostic and come from Key Vault. + * + * @param azureEnvironment The Azure environment (included for consistency, currently unused) + * @return CompletableFuture containing LabResponse for an ADFS federated user + */ + public static CompletableFuture getDefaultAdfsUserAsync(String azureEnvironment) { + log.debug("Getting default ADFS user for environment: {}", azureEnvironment); + + if (AzureEnvironment.AZURE.equals(azureEnvironment)) { + return getDefaultUserAsync(); + } else if (AzureEnvironment.AZURE_US_GOVERNMENT.equals(azureEnvironment)) { + return getArlingtonADFSUserAsync(); + } else { + log.error("Unsupported Azure environment: {}", azureEnvironment); + throw new IllegalArgumentException("Unsupported Azure environment: " + azureEnvironment); + } + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java new file mode 100644 index 00000000..127f42db --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +/** + * Exception thrown when a lab user matching the query cannot be found. + */ +public class LabUserNotFoundException extends RuntimeException { + + private final UserQuery query; + + public LabUserNotFoundException(UserQuery query, String message) { + super(message); + this.query = query; + } + + public UserQuery getQuery() { + return query; + } + + @Override + public String toString() { + return String.format("LabUserNotFoundException{query=%s, message=%s}", + query, getMessage()); + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java new file mode 100644 index 00000000..f9459955 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi2; + +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.*; + +public class UserQuery { + private UserType userType; + private B2CIdentityProvider b2cIdentityProvider; + private FederationProvider federationProvider; + private String azureEnvironment; + + // Getters and Setters + public UserType getUserType() { return userType; } + public void setUserType(UserType userType) { this.userType = userType; } + + public B2CIdentityProvider getB2cIdentityProvider() { return b2cIdentityProvider; } + public void setB2cIdentityProvider(B2CIdentityProvider b2cIdentityProvider) { + this.b2cIdentityProvider = b2cIdentityProvider; + } + + public FederationProvider getFederationProvider() { return federationProvider; } + public void setFederationProvider(FederationProvider federationProvider) { + this.federationProvider = federationProvider; + } + + public String getAzureEnvironment() { return azureEnvironment; } + public void setAzureEnvironment(String azureEnvironment) { + this.azureEnvironment = azureEnvironment; + } + + public static UserQuery b2cLocalAccountUserQuery() { + UserQuery query = new UserQuery(); + query.setUserType(UserType.B2C); + query.setB2cIdentityProvider(B2CIdentityProvider.LOCAL); + return query; + } + + public static UserQuery arlingtonUserQuery() { + UserQuery query = new UserQuery(); + query.setUserType(UserType.CLOUD); + query.setAzureEnvironment(AzureEnvironment.AZURE_US_GOVERNMENT); + return query; + } + + @Override + public int hashCode() { + // Implement proper hashCode for caching + int result = 17; + result = 31 * result + (userType != null ? userType.hashCode() : 0); + result = 31 * result + (azureEnvironment != null ? azureEnvironment.hashCode() : 0); + result = 31 * result + (b2cIdentityProvider != null ? b2cIdentityProvider.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + UserQuery other = (UserQuery) obj; + return java.util.Objects.equals(userType, other.userType) && + java.util.Objects.equals(azureEnvironment, other.azureEnvironment) && + java.util.Objects.equals(b2cIdentityProvider, other.b2cIdentityProvider); + } +} \ No newline at end of file From 2774c9f6a8b457cb635a9e282fd9e65bd96f9a86 Mon Sep 17 00:00:00 2001 From: avdunn Date: Tue, 18 Nov 2025 16:45:14 -0800 Subject: [PATCH 02/14] Non-working, removed all references to old test infrastructure classes --- .../AcquireTokenInteractiveIT.java | 188 +++++------ .../AcquireTokenSilentIT.java | 115 ++++--- .../AuthorizationCodeIT.java | 141 ++++---- .../AzureEnvironmentIT.java | 26 +- .../ClientCredentialsIT.java | 2 - .../DeviceCodeIT.java | 121 +++---- .../EnvironmentsProvider.java | 4 +- .../HttpClientIT.java | 33 +- .../OnBehalfOfIT.java | 7 +- .../RefreshTokenIT.java | 10 +- .../SeleniumTest.java | 17 +- .../TokenCacheIT.java | 301 +++++++++--------- .../UsernamePasswordIT.java | 14 +- .../msal4j/labapi2/AppCredentialProvider.java | 26 +- .../microsoft/aad/msal4j/labapi2/Config.java | 30 +- ...LabApiConstants.java => LabConstants.java} | 5 +- .../aad/msal4j/labapi2/LabServiceApi.java | 244 +++++++------- .../msal4j/labapi2/LabServiceParameters.java | 4 + .../microsoft/aad/msal4j/labapi2/LabUser.java | 42 +++ .../aad/msal4j/labapi2/LabUserHelper.java | 140 ++++---- .../infrastructure/SeleniumExtensions.java | 12 +- .../infrastructure/UserInformationFields.java | 31 +- 22 files changed, 785 insertions(+), 728 deletions(-) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/{LabApiConstants.java => LabConstants.java} (87%) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index 3b128d7f..30f7cca3 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -3,14 +3,12 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -21,8 +19,6 @@ import java.net.URI; import java.net.URL; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ExecutionException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -31,12 +27,6 @@ class AcquireTokenInteractiveIT extends SeleniumTest { private Config cfg; - - @BeforeAll - public void setupUserProvider() { - setUpLapUserProvider(); - } - @AfterEach public void stopBrowser() { cleanUp(); @@ -52,16 +42,19 @@ public void startBrowser() { void acquireTokenInteractive_ManagedUser(String environment) { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); - assertAcquireTokenCommon(user, cfg.commonAuthority(), cfg.graphDefaultScope()); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), cfg.commonAuthority(), cfg.graphDefaultScope()); } - @Test() - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenInteractive_ADFSv2019_OnPrem() { - User user = labUserProvider.getOnPremAdfsUser(FederationProvider.ADFS_2019); - assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE); - } + // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET +// @Test() +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void acquireTokenInteractive_ADFSv2019_OnPrem() { +// LabResponse labResponse = LabUserHelper.getOnPremAdfsUser(LabServiceParameters.FederationProvider.ADFS_V2019); +// LabUser user = labResponse.getUser(); +// assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE); +// } @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") @@ -69,26 +62,30 @@ void acquireTokenInteractive_ADFSv2019_OnPrem() { void acquireTokenInteractive_ADFSv2019_Federated(String environment) { cfg = new Config(environment); - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_2019); - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); + LabUser user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), cfg.organizationsAuthority(), cfg.graphDefaultScope()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenInteractive_ADFSv4_Federated(String environment) { - cfg = new Config(environment); - - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_4); - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); - } + // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET +// @ParameterizedTest +// @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void acquireTokenInteractive_ADFSv4_Federated(String environment) { +// cfg = new Config(environment); +// +// LabResponse labResponse = LabUserHelper.getFederatedAdfsUser(environment, LabServiceParameters.FederationProvider.ADFS_V4); +// LabUser user = labResponse.getUser(); +// assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); +// } @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") void acquireTokenWithAuthorizationCode_B2C_Local(String environment) { cfg = new Config(environment); - User user = labUserProvider.getB2cUser(cfg.azureEnvironment, B2CProvider.LOCAL); + LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); + LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY); } @@ -97,64 +94,69 @@ void acquireTokenWithAuthorizationCode_B2C_Local(String environment) { void acquireTokenWithAuthorizationCode_B2C_LegacyFormat(String environment) { cfg = new Config(environment); - User user = labUserProvider.getB2cUser(cfg.azureEnvironment, B2CProvider.LOCAL); + LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); + LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT); } - @Test - void acquireTokenInteractive_ManagedUser_InstanceAware() { - cfg = new Config(AzureEnvironment.AZURE); - - User user = labUserProvider.getDefaultUser(AzureEnvironment.AZURE_US_GOVERNMENT); - assertAcquireTokenInstanceAware(user); - } - - @Test - void acquireTokenInteractive_Ciam() { - User user = labUserProvider.getCiamCudUser(); - - Map extraQueryParameters = new HashMap<>(); - - PublicClientApplication pca; - try { - pca = PublicClientApplication.builder( - user.getAppId()). - authority("https://" + user.getLabName() + ".ciamlogin.com/") - .build(); - } catch (MalformedURLException ex) { - throw new RuntimeException(ex.getMessage()); - } - - IAuthenticationResult result; - try { - URI url = new URI("http://localhost:8080"); - - SystemBrowserOptions browserOptions = - SystemBrowserOptions - .builder() - .openBrowserAction(new SeleniumOpenBrowserAction(user, pca)) - .build(); - - InteractiveRequestParameters parameters = InteractiveRequestParameters - .builder(url) - .scopes(Collections.singleton(TestConstants.USER_READ_SCOPE)) - .extraQueryParameters(extraQueryParameters) - .systemBrowserOptions(browserOptions) - .build(); - - result = pca.acquireToken(parameters).get(); - - } catch (Exception e) { - LOG.error("Error acquiring token with authCode: {}", e.getMessage()); - throw new RuntimeException("Error acquiring token with authCode: " + e.getMessage()); - } - - IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); - assertEquals(user.getUpn(), result.account().username()); - } - - private void assertAcquireTokenCommon(User user, String authority, String scope) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), authority); + // TODO: labapi2 needs cross-cloud instance aware test configuration - will be pulled from MSAL.NET +// @Test +// void acquireTokenInteractive_ManagedUser_InstanceAware() { +// cfg = new Config(AzureEnvironment.AZURE); +// +// LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE_US_GOVERNMENT); +// LabUser user = labResponse.getUser(); +// assertAcquireTokenInstanceAware(user); +// } + + // TODO: labapi2 doesn't have CIAM CUD user configuration yet - will be pulled from MSAL.NET +// @Test +// void acquireTokenInteractive_Ciam() { +// LabResponse labResponse = LabUserHelper.getCiamCudUser(); +// LabUser user = labResponse.getUser(); +// +// Map extraQueryParameters = new HashMap<>(); +// +// PublicClientApplication pca; +// try { +// pca = PublicClientApplication.builder( +// user.getAppId()). +// authority("https://" + user.getLabName() + ".ciamlogin.com/") +// .build(); +// } catch (MalformedURLException ex) { +// throw new RuntimeException(ex.getMessage()); +// } +// +// IAuthenticationResult result; +// try { +// URI url = new URI("http://localhost:8080"); +// +// SystemBrowserOptions browserOptions = +// SystemBrowserOptions +// .builder() +// .openBrowserAction(new SeleniumOpenBrowserAction(user, pca)) +// .build(); +// +// InteractiveRequestParameters parameters = InteractiveRequestParameters +// .builder(url) +// .scopes(Collections.singleton(TestConstants.USER_READ_SCOPE)) +// .extraQueryParameters(extraQueryParameters) +// .systemBrowserOptions(browserOptions) +// .build(); +// +// result = pca.acquireToken(parameters).get(); +// +// } catch (Exception e) { +// LOG.error("Error acquiring token with authCode: {}", e.getMessage()); +// throw new RuntimeException("Error acquiring token with authCode: " + e.getMessage()); +// } +// +// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); +// assertEquals(user.getUpn(), result.account().username()); +// } + + private void assertAcquireTokenCommon(LabUser user, String appId, String authority, String scope) { + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, authority); IAuthenticationResult result = acquireTokenInteractive( user, @@ -165,7 +167,7 @@ private void assertAcquireTokenCommon(User user, String authority, String scope) assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenB2C(User user, String authority) { + private void assertAcquireTokenB2C(LabUser user, String authority) { PublicClientApplication pca; try { @@ -181,8 +183,8 @@ private void assertAcquireTokenB2C(User user, String authority) { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - private void assertAcquireTokenInstanceAware(User user) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + user.getTenantID()); + private void assertAcquireTokenInstanceAware(LabUser user) { + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + user.getTenantId()); IAuthenticationResult result = acquireTokenInteractive_instanceAware(user, pca, cfg.graphDefaultScope()); @@ -213,7 +215,7 @@ private IAuthenticationResult acquireTokenSilently(IPublicClientApplication pca, } private IAuthenticationResult acquireTokenInteractive( - User user, + LabUser user, PublicClientApplication pca, String scope) { @@ -243,7 +245,7 @@ private IAuthenticationResult acquireTokenInteractive( } private IAuthenticationResult acquireTokenInteractive_instanceAware( - User user, + LabUser user, PublicClientApplication pca, String scope) { @@ -274,10 +276,10 @@ private IAuthenticationResult acquireTokenInteractive_instanceAware( class SeleniumOpenBrowserAction implements OpenBrowserAction { - private User user; + private LabUser user; private PublicClientApplication pca; - SeleniumOpenBrowserAction(User user, PublicClientApplication pca) { + SeleniumOpenBrowserAction(LabUser user, PublicClientApplication pca) { this.user = user; this.pca = pca; } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index a649b348..05c30735 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -3,13 +3,12 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.api.BeforeAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -23,14 +22,8 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AcquireTokenSilentIT { - private LabUserProvider labUserProvider; - private Config cfg; - @BeforeAll - void setUp() { - labUserProvider = LabUserProvider.getInstance(); - } @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") @@ -53,11 +46,11 @@ void acquireTokenSilent_LabAuthority_TokenNotRefreshed(String environment) throw // Access token should be returned from cache, and not using refresh token - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); - + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -79,10 +72,11 @@ void acquireTokenSilent_LabAuthority_TokenNotRefreshed(String environment) throw void acquireTokenSilent_ForceRefresh(String environment) throws Exception { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -99,29 +93,31 @@ void acquireTokenSilent_ForceRefresh(String environment) throws Exception { assertEquals(TokenSource.IDENTITY_PROVIDER, resultAfterRefresh.metadata().tokenSource()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenSilent_MultipleAccountsInCache_UseCorrectAccount(String environment) throws Exception { - cfg = new Config(environment); - - IPublicClientApplication pca = getPublicClientApplicationWithTokensInCache(); - - // get lab user for different account - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_4); - - // acquire token for different account - acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); - - Set accounts = pca.getAccounts().join(); - IAccount account = accounts.stream().filter( - x -> x.username().equalsIgnoreCase( - user.getUpn())).findFirst().orElse(null); - - IAuthenticationResult result = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), false); - assertResultNotNull(result); - assertEquals(result.account().username(), user.getUpn()); - } + // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET +// @ParameterizedTest +// @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void acquireTokenSilent_MultipleAccountsInCache_UseCorrectAccount(String environment) throws Exception { +// cfg = new Config(environment); +// +// IPublicClientApplication pca = getPublicClientApplicationWithTokensInCache(); +// +// // get lab user for different account +// LabResponse labResponse = LabUserHelper.getFederatedAdfsUser(environment, LabServiceParameters.FederationProvider.ADFS_V4); +// LabUser user = labResponse.getUser(); +// +// // acquire token for different account +// acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); +// +// Set accounts = pca.getAccounts().join(); +// IAccount account = accounts.stream().filter( +// x -> x.username().equalsIgnoreCase( +// user.getUpn())).findFirst().orElse(null); +// +// IAuthenticationResult result = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), false); +// assertResultNotNull(result); +// assertEquals(result.account().username(), user.getUpn()); +// } @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") @@ -129,15 +125,11 @@ void acquireTokenSilent_MultipleAccountsInCache_UseCorrectAccount(String environ void acquireTokenSilent_ADFS2019(String environment) throws Exception { cfg = new Config(environment); - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, cfg.azureEnvironment); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.ADFS_2019); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.FEDERATED); - - User user = labUserProvider.getLabUser(query); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -222,10 +214,11 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrow void acquireTokenSilent_WithRefreshOn(String environment) throws Exception { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -269,10 +262,11 @@ void acquireTokenSilent_WithRefreshOn(String environment) throws Exception { void acquireTokenSilent_TenantAsParameter(String environment) throws Exception { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -300,10 +294,11 @@ void acquireTokenSilent_TenantAsParameter(String environment) throws Exception { @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") void acquireTokenSilent_emptyStringScope(String environment) throws Exception { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -321,11 +316,12 @@ void acquireTokenSilent_emptyStringScope(String environment) throws Exception { @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") void acquireTokenSilent_emptyScopeSet(String environment) throws Exception { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -350,11 +346,12 @@ void acquireTokenSilent_emptyScopeSet(String environment) throws Exception { @Test public void acquireTokenSilent_ClaimsForceRefresh() throws Exception { cfg = new Config(AzureEnvironment.AZURE); - User user = labUserProvider.getDefaultUser(AzureEnvironment.AZURE); + LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabUser user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -403,10 +400,11 @@ private IConfidentialClientApplication getConfidentialClientApplications() throw } private void acquireTokenSilent_returnCachedTokens(String authority) throws Exception { - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(cfg.azureEnvironment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(authority). build(); @@ -426,10 +424,11 @@ private void acquireTokenSilent_returnCachedTokens(String authority) throws Exce private IPublicClientApplication getPublicClientApplicationWithTokensInCache() throws Exception { - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(cfg.azureEnvironment); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); @@ -445,7 +444,7 @@ private IAuthenticationResult acquireTokenSilently(IPublicClientApplication pca, .get(); } - private IAuthenticationResult acquireTokenUsernamePassword(User user, IPublicClientApplication pca, String scope) throws InterruptedException, ExecutionException { + private IAuthenticationResult acquireTokenUsernamePassword(LabUser user, IPublicClientApplication pca, String scope) throws InterruptedException, ExecutionException { Map map = new HashMap<>(); map.put("test","test"); return pca.acquireToken(UserNamePasswordParameters. diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index c0ed99a7..c2b3fd19 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -3,9 +3,8 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; @@ -32,10 +31,6 @@ class AuthorizationCodeIT extends SeleniumTest { private Config cfg; - @BeforeAll - public void setupUserProvider() { - setUpLapUserProvider(); - } @AfterEach public void stopBrowser() { @@ -52,16 +47,19 @@ public void startBrowser() { public void acquireTokenWithAuthorizationCode_ManagedUser(String environment) { cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); assertAcquireTokenAAD(user, null); } - @Test - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - public void acquireTokenWithAuthorizationCode_ADFSv2019_OnPrem() { - User user = labUserProvider.getOnPremAdfsUser(FederationProvider.ADFS_2019); - assertAcquireTokenADFS2019(user); - } + // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET +// @Test +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// public void acquireTokenWithAuthorizationCode_ADFSv2019_OnPrem() { +// LabResponse labResponse = LabUserHelper.getOnPremAdfsUser(LabServiceParameters.FederationProvider.ADFS_V2019); +// LabUser user = labResponse.getUser(); +// assertAcquireTokenADFS2019(user); +// } @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") @@ -69,68 +67,74 @@ public void acquireTokenWithAuthorizationCode_ADFSv2019_OnPrem() { public void acquireTokenWithAuthorizationCode_ADFSv2019_Federated(String environment) { cfg = new Config(environment); - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_2019); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); + LabUser user = labResponse.getUser(); assertAcquireTokenAAD(user, null); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - public void acquireTokenWithAuthorizationCode_ADFSv4_Federated(String environment) { - cfg = new Config(environment); - - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_4); - - assertAcquireTokenAAD(user, null); - } + // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET +// @ParameterizedTest +// @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// public void acquireTokenWithAuthorizationCode_ADFSv4_Federated(String environment) { +// cfg = new Config(environment); +// +// LabResponse labResponse = LabUserHelper.getFederatedAdfsUser(environment, LabServiceParameters.FederationProvider.ADFS_V4); +// LabUser user = labResponse.getUser(); +// +// assertAcquireTokenAAD(user, null); +// } @ParameterizedTest @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") public void acquireTokenWithAuthorizationCode_B2C_Local(String environment) { cfg = new Config(environment); - User user = labUserProvider.getB2cUser(environment, B2CProvider.LOCAL); + LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); + LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user); } - @Test - public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { - String authorityCud = "https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/"; - User user = labUserProvider.getCiamCudUser(); - - PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - oidcAuthority(authorityCud). - build(); - - assertEquals("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/.well-known/openid-configuration", - pca.authenticationAuthority.canonicalAuthorityUrl.toString()); - assertEquals("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/oauth2/v2.0/authorize", - pca.authenticationAuthority.authorizationEndpoint); - - String authCode = acquireAuthorizationCodeAutomated(user, pca, null); - - IAuthenticationResult result = pca.acquireToken(AuthorizationCodeParameters - .builder(authCode, - new URI(TestConstants.LOCALHOST + httpListener.port())) - .scopes(Collections.singleton("user.read")) - .build()) - .get(); - - IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); - assertEquals(user.getUpn(), result.account().username()); - - IAuthenticationResult resultSilent = pca.acquireTokenSilently(SilentParameters - .builder(Collections.singleton("user.read"), result.account()) - .build()) - .get(); - - IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); - assertEquals(resultSilent.accessToken(), result.accessToken()); - assertEquals(resultSilent.account().username(), result.account().username()); - } - - private void assertAcquireTokenADFS2019(User user) { + // TODO: labapi2 doesn't have CIAM CUD user configuration yet - will be pulled from MSAL.NET +// @Test +// public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { +// String authorityCud = "https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/"; +// LabResponse labResponse = LabUserHelper.getCiamCudUser(); +// LabUser user = labResponse.getUser(); +// +// PublicClientApplication pca = PublicClientApplication.builder( +// user.getAppId()). +// oidcAuthority(authorityCud). +// build(); +// +// assertEquals("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/.well-known/openid-configuration", +// pca.authenticationAuthority.canonicalAuthorityUrl.toString()); +// assertEquals("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/oauth2/v2.0/authorize", +// pca.authenticationAuthority.authorizationEndpoint); +// +// String authCode = acquireAuthorizationCodeAutomated(user, pca, null); +// +// IAuthenticationResult result = pca.acquireToken(AuthorizationCodeParameters +// .builder(authCode, +// new URI(TestConstants.LOCALHOST + httpListener.port())) +// .scopes(Collections.singleton("user.read")) +// .build()) +// .get(); +// +// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); +// assertEquals(user.getUpn(), result.account().username()); +// +// IAuthenticationResult resultSilent = pca.acquireTokenSilently(SilentParameters +// .builder(Collections.singleton("user.read"), result.account()) +// .build()) +// .get(); +// +// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); +// assertEquals(resultSilent.accessToken(), result.accessToken()); +// assertEquals(resultSilent.account().username(), result.account().username()); +// } + + private void assertAcquireTokenADFS2019(LabUser user) { PublicClientApplication pca; try { pca = PublicClientApplication.builder( @@ -151,7 +155,7 @@ private void assertAcquireTokenADFS2019(User user) { assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenAAD(User user, Map> parameters) { + private void assertAcquireTokenAAD(LabUser user, Map> parameters) { PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), cfg.commonAuthority()); @@ -165,10 +169,11 @@ private void assertAcquireTokenAAD(User user, Map> parameter assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenB2C(User user) { + private void assertAcquireTokenB2C(LabUser user) { - String appId = LabService.getSecret(TestConstants.B2C_CONFIDENTIAL_CLIENT_LAB_APP_ID); - String appSecret = LabService.getSecret(TestConstants.B2C_CONFIDENTIAL_CLIENT_APP_SECRETID); + KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); + String appId = keyVaultSecretsProvider.getSecretByName(TestConstants.B2C_CONFIDENTIAL_CLIENT_LAB_APP_ID).getValue(); + String appSecret = keyVaultSecretsProvider.getSecretByName(TestConstants.B2C_CONFIDENTIAL_CLIENT_APP_SECRETID).getValue(); ConfidentialClientApplication cca; try { @@ -226,7 +231,7 @@ private IAuthenticationResult acquireTokenInteractiveB2C(ConfidentialClientAppli } private String acquireAuthorizationCodeAutomated( - User user, + LabUser user, AbstractClientApplicationBase app, Map> parameters) { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java index b7d24a74..d21ede70 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java @@ -3,27 +3,21 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.BeforeAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Collections; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AzureEnvironmentIT { - private LabUserProvider labUserProvider; - @BeforeAll - void setUp() { - labUserProvider = LabUserProvider.getInstance(); - } - - @Test - void acquireTokenWithUsernamePassword_AzureChina() throws Exception { - assertAcquireTokenCommon(AzureEnvironment.AZURE_CHINA); - } + // TODO: labapi2 doesn't have Azure China user configuration yet - will be pulled from MSAL.NET +// @Test +// void acquireTokenWithUsernamePassword_AzureChina() throws Exception { +// assertAcquireTokenCommon(AzureEnvironment.AZURE_CHINA); +// } @Test void acquireTokenWithUsernamePassword_AzureGovernment() throws Exception { @@ -31,12 +25,12 @@ void acquireTokenWithUsernamePassword_AzureGovernment() throws Exception { } private void assertAcquireTokenCommon(String azureEnvironment) throws Exception { - User user = labUserProvider.getUserByAzureEnvironment(azureEnvironment); - - App app = LabService.getApp(user.getAppId()); + LabResponse labResponse = LabUserHelper.getDefaultUser(azureEnvironment); + LabUser user = labResponse.getUser(); + LabApp app = labResponse.getApp(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + app.getAppId()). authority(app.getAuthority() + "organizations/"). build(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java index 0bb91204..366fdfd7 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java @@ -27,13 +27,11 @@ class ClientCredentialsIT { private IClientCertificate certificate; private KeyVaultSecretsProvider keyVaultSecretsProvider; - private LabServiceApi labServiceApi; @BeforeAll void init() throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, IOException { certificate = CertificateHelper.getClientCertificate(); keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - labServiceApi = new LabServiceApi(); } @Test diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index 94801e29..fd387e69 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -3,8 +3,8 @@ package com.microsoft.aad.msal4j; +import com.microsoft.aad.msal4j.labapi2.*; import infrastructure.SeleniumExtensions; -import labapi.*; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -25,12 +25,10 @@ class DeviceCodeIT { private static final Logger LOG = LoggerFactory.getLogger(DeviceCodeIT.class); - private LabUserProvider labUserProvider; private WebDriver seleniumDriver; @BeforeAll void setUp() { - labUserProvider = LabUserProvider.getInstance(); seleniumDriver = SeleniumExtensions.createDefaultWebDriver(); } @@ -39,9 +37,10 @@ void setUp() { void DeviceCodeFlowADTest(String environment) throws Exception { Config cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), cfg.commonAuthority()); + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(labResponse.getApp().getAppId(), cfg.commonAuthority()); Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> runAutomatedDeviceCodeFlow(deviceCode, user); @@ -54,61 +53,65 @@ void DeviceCodeFlowADTest(String environment) throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - @Test() - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void DeviceCodeFlowADFSv2019Test() throws Exception { - - User user = labUserProvider.getOnPremAdfsUser(FederationProvider.ADFS_2019); - - PublicClientApplication pca = PublicClientApplication.builder( - TestConstants.ADFS_APP_ID). - authority(TestConstants.ADFS_AUTHORITY).validateAuthority(false). - build(); - - Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { - runAutomatedDeviceCodeFlow(deviceCode, user); - }; - - IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters - .builder(Collections.singleton(TestConstants.ADFS_SCOPE), - deviceCodeConsumer) - .build()) - .get(); - - IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); - } - - //TODO: This test is failing intermittently in the pipeline runs for the same commit, but always passes locally. Disabling until we can investigate more. + // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET +// @Test() +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void DeviceCodeFlowADFSv2019Test() throws Exception { +// +// LabResponse labResponse = LabUserHelper.getOnPremAdfsUser(LabServiceParameters.FederationProvider.ADFS_V2019); +// LabUser user = labResponse.getUser(); +// +// PublicClientApplication pca = PublicClientApplication.builder( +// TestConstants.ADFS_APP_ID). +// authority(TestConstants.ADFS_AUTHORITY).validateAuthority(false). +// build(); +// +// Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { +// runAutomatedDeviceCodeFlow(deviceCode, user); +// }; +// +// IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters +// .builder(Collections.singleton(TestConstants.ADFS_SCOPE), +// deviceCodeConsumer) +// .build()) +// .get(); +// +// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); +// } + + // TODO: labapi2 doesn't have MSA user configuration yet - will be pulled from MSAL.NET + // NOTE: This test was also failing intermittently in the pipeline runs for the same commit, but always passed locally. //@Test() - void DeviceCodeFlowMSATest() throws Exception { - - User user = labUserProvider.getMSAUser(); - - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.CONSUMERS_AUTHORITY); - - Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { - runAutomatedDeviceCodeFlow(deviceCode, user); - }; - - IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters - .builder(Collections.singleton(""), - deviceCodeConsumer) - .build()) - .get(); - - assertNotNull(result); - assertNotNull(result.accessToken()); - - result = pca.acquireTokenSilently(SilentParameters. - builder(Collections.singleton(""), result.account()). - build()) - .get(); - - assertNotNull(result); - assertNotNull(result.accessToken()); - } - - private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, User user) { +// void DeviceCodeFlowMSATest() throws Exception { +// +// LabResponse labResponse = LabUserHelper.getMSAUser(); +// LabUser user = labResponse.getUser(); +// +// PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.CONSUMERS_AUTHORITY); +// +// Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { +// runAutomatedDeviceCodeFlow(deviceCode, user); +// }; +// +// IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters +// .builder(Collections.singleton(""), +// deviceCodeConsumer) +// .build()) +// .get(); +// +// assertNotNull(result); +// assertNotNull(result.accessToken()); +// +// result = pca.acquireTokenSilently(SilentParameters. +// builder(Collections.singleton(""), result.account()). +// build()) +// .get(); +// +// assertNotNull(result); +// assertNotNull(result.accessToken()); +// } + + private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { boolean isRunningLocally = true;//!Strings.isNullOrEmpty( //System.getenv(TestConstants.LOCAL_FLAG_ENV_VAR)); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java index f27c69f4..1d63e442 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java @@ -8,7 +8,7 @@ public class EnvironmentsProvider { public static Object[][] createData() { return new Object[][]{ - {AzureEnvironment.AZURE}, - {AzureEnvironment.AZURE_US_GOVERNMENT}}; + {AzureEnvironment.AZURE}}; + //{AzureEnvironment.AZURE_US_GOVERNMENT}};//Might be removed entirely under new system } } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java index 09567056..398c1bd2 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java @@ -3,11 +3,9 @@ package com.microsoft.aad.msal4j; -import labapi.LabUserProvider; -import labapi.User; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.BeforeAll; import java.util.Collections; import java.util.concurrent.ExecutionException; @@ -19,37 +17,34 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class HttpClientIT { - private LabUserProvider labUserProvider; - - @BeforeAll - void setUp() { - labUserProvider = LabUserProvider.getInstance(); - } @Test void acquireToken_okHttpClient() throws Exception { - User user = labUserProvider.getDefaultUser(); - assertAcquireTokenCommon(user, new OkHttpClientAdapter()); + LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabUser user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new OkHttpClientAdapter()); } @Test void acquireToken_apacheHttpClient() throws Exception { - User user = labUserProvider.getDefaultUser(); - assertAcquireTokenCommon(user, new ApacheHttpClientAdapter()); + LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabUser user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new ApacheHttpClientAdapter()); } @Test void acquireToken_readTimeout() throws Exception { - User user = labUserProvider.getDefaultUser(); + LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabUser user = labResponse.getUser(); //Set a 1ms read timeout, which will almost certainly occur before the service can respond - assertAcquireTokenCommon_WithTimeout(user, 1); + assertAcquireTokenCommon_WithTimeout(user, labResponse.getApp().getAppId(), 1); } - private void assertAcquireTokenCommon(User user, IHttpClient httpClient) + private void assertAcquireTokenCommon(LabUser user, String appId, IHttpClient httpClient) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + appId). authority(TestConstants.ORGANIZATIONS_AUTHORITY). httpClient(httpClient). build(); @@ -67,10 +62,10 @@ private void assertAcquireTokenCommon(User user, IHttpClient httpClient) assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenCommon_WithTimeout(User user, int readTimeout) + private void assertAcquireTokenCommon_WithTimeout(LabUser user, String appId, int readTimeout) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + appId). authority(TestConstants.ORGANIZATIONS_AUTHORITY). readTimeoutForDefaultHttpClient(readTimeout). build(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java index 6f352fdf..45743e65 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java @@ -3,8 +3,7 @@ package com.microsoft.aad.msal4j; -import labapi.*; -import org.junit.jupiter.api.Test; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -130,8 +129,8 @@ private void assertResultNotNull(IAuthenticationResult result) { private String getAccessToken() throws Exception { - LabUserProvider labUserProvider = LabUserProvider.getInstance(); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(cfg.azureEnvironment); + LabUser user = labResponse.getUser(); String clientId = cfg.appProvider.getAppId(); String apiReadScope = cfg.appProvider.getOboAppIdURI() + "/user_impersonation"; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java index 37984846..64f7af8a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java @@ -3,8 +3,7 @@ package com.microsoft.aad.msal4j; -import labapi.LabUserProvider; -import labapi.User; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; @@ -13,7 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Collections; -import java.util.concurrent.ExecutionException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class RefreshTokenIT { @@ -23,11 +21,11 @@ class RefreshTokenIT { private Config cfg; private void setUp(String environment) throws Exception { - LabUserProvider labUserProvider = LabUserProvider.getInstance(); - User user = labUserProvider.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabUser user = labResponse.getUser(); pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(cfg.organizationsAuthority()). build(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java index 907e1192..50c9788a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java @@ -3,22 +3,15 @@ package com.microsoft.aad.msal4j; +import com.microsoft.aad.msal4j.labapi2.LabUser; import infrastructure.SeleniumExtensions; -import labapi.B2CProvider; -import labapi.LabUserProvider; -import labapi.User; import org.openqa.selenium.WebDriver; abstract class SeleniumTest { - protected LabUserProvider labUserProvider; WebDriver seleniumDriver; HttpListener httpListener; - public void setUpLapUserProvider() { - labUserProvider = LabUserProvider.getInstance(); - } - public void cleanUp() { seleniumDriver.quit(); if (httpListener != null) { @@ -30,17 +23,17 @@ public void startUpBrowser() { seleniumDriver = SeleniumExtensions.createDefaultWebDriver(); } - void runSeleniumAutomatedLogin(User user, AbstractClientApplicationBase app) { + void runSeleniumAutomatedLogin(LabUser user, AbstractClientApplicationBase app) { AuthorityType authorityType = app.authenticationAuthority.authorityType; if (authorityType == AuthorityType.B2C) { switch (user.getB2cProvider().toLowerCase()) { - case B2CProvider.LOCAL: + case "local": SeleniumExtensions.performLocalLogin(seleniumDriver, user); break; - case B2CProvider.GOOGLE: + case "google": SeleniumExtensions.performGoogleLogin(seleniumDriver, user); break; - case B2CProvider.FACEBOOK: + case "facebook": SeleniumExtensions.performFacebookLogin(seleniumDriver, user); break; } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index 7d5a48e7..a8a900e0 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -3,33 +3,23 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Set; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TokenCacheIT { - private LabUserProvider labUserProvider; - - @BeforeAll - void setUp() { - labUserProvider = LabUserProvider.getInstance(); - } - @Test void singleAccountInCache_RemoveAccountTest() throws Exception { - User user = labUserProvider.getDefaultUser(); + LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( user.getAppId()). @@ -59,145 +49,152 @@ void singleAccountInCache_RemoveAccountTest() throws Exception { assertEquals(pca.getAccounts().join().size(), 0); } - @Test - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void twoAccountsInCache_RemoveAccountTest() throws Exception { - - User managedUser = labUserProvider.getDefaultUser(); - - PublicClientApplication pca = PublicClientApplication.builder( - managedUser.getAppId()). - authority(TestConstants.ORGANIZATIONS_AUTHORITY). - build(); - - assertEquals(pca.getAccounts().join().size(), 0); - - pca.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), - managedUser.getUpn(), - managedUser.getPassword().toCharArray()) - .build()) - .get(); - - assertEquals(pca.getAccounts().join().size(), 1); - - // get lab user for different account - User adfsUser = labUserProvider.getFederatedAdfsUser(FederationProvider.ADFS_4); - - // acquire token for different account - pca.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), - adfsUser.getUpn(), - adfsUser.getPassword().toCharArray()) - .build()) - .get(); - - assertEquals(pca.getAccounts().join().size(), 2); - - Set accounts = pca.getAccounts().join(); - IAccount accountLabResponse1 = accounts.stream().filter( - x -> x.username().equalsIgnoreCase( - managedUser.getUpn())).findFirst().orElse(null); - - pca.removeAccount(accountLabResponse1).join(); - - assertEquals(pca.getAccounts().join().size(), 1); - - IAccount accountLabResponse2 = pca.getAccounts().get().iterator().next(); - - // Check that the right account was left in the cache - assertEquals(accountLabResponse2.username(), adfsUser.getUpn()); - } - - @Test - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exception { - - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.GUEST); - - User guestUser = labUserProvider.getLabUser(query); - Lab lab = LabService.getLab(guestUser.getLabName()); - - String dataToInitCache = TestHelper.readResource( - this.getClass(), - "/cache_data/remove-account-test-cache.json"); - - // check that cache is empty - assertEquals(dataToInitCache, ""); - - ITokenCacheAccessAspect persistenceAspect = new TokenPersistence(dataToInitCache); - - // acquire tokens for home tenant, and serialize cache - PublicClientApplication pca = PublicClientApplication.builder( - guestUser.getAppId()). - authority(TestConstants.ORGANIZATIONS_AUTHORITY) - .setTokenCacheAccessAspect(persistenceAspect) - .build(); - - pca.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), - guestUser.getHomeUPN(), - guestUser.getPassword().toCharArray()) - .build()) - .get(); - - String guestTenantAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + lab.getTenantId(); - - // initialize pca with tenant where user is guest, deserialize cache, and acquire second token - PublicClientApplication pca2 = PublicClientApplication.builder( - guestUser.getAppId()). - authority(guestTenantAuthority). - setTokenCacheAccessAspect(persistenceAspect). - build(); - - pca2.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), - guestUser.getHomeUPN(), - guestUser.getPassword().toCharArray()) - .build()) - .get(); - - // There should be two tokens in cache, with same accounts except for tenant - assertEquals(pca2.getAccounts().join().iterator().next().getTenantProfiles().size(), 2); - - IAccount account = pca2.getAccounts().get().iterator().next(); - - // RemoveAccount should remove both cache entities - pca2.removeAccount(account).join(); - - assertEquals(0, pca2.getAccounts().join().size()); - - //clean up file - TestHelper.deleteFileContent( - this.getClass(), - "/cache_data/remove-account-test-cache.json"); - } - - @Test - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void retrieveAccounts_ADFSOnPrem() throws Exception { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.ADFS_2019); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.ON_PREM); - - User user = labUserProvider.getLabUser(query); - - PublicClientApplication pca = PublicClientApplication.builder( - TestConstants.ADFS_APP_ID). - authority(TestConstants.ADFS_AUTHORITY). - build(); - - pca.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(TestConstants.ADFS_SCOPE), - user.getUpn(), - user.getPassword().toCharArray()) - .build()) - .get(); - - assertNotNull(pca.getAccounts().join().iterator().next()); - assertEquals(pca.getAccounts().join().size(), 1); - } + // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET +// @Test +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void twoAccountsInCache_RemoveAccountTest() throws Exception { +// +// LabResponse managedResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); +// LabUser managedUser = managedResponse.getUser(); +// +// PublicClientApplication pca = PublicClientApplication.builder( +// managedResponse.getApp().getAppId()). +// authority(TestConstants.ORGANIZATIONS_AUTHORITY). +// build(); +// +// assertEquals(pca.getAccounts().join().size(), 0); +// +// pca.acquireToken(UserNamePasswordParameters. +// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), +// managedUser.getUpn(), +// managedUser.getPassword().toCharArray()) +// .build()) +// .get(); +// +// assertEquals(pca.getAccounts().join().size(), 1); +// +// // get lab user for different account +// LabResponse adfsResponse = LabUserHelper.getFederatedAdfsUser(AzureEnvironment.AZURE, LabServiceParameters.FederationProvider.ADFS_V4); +// LabUser adfsUser = adfsResponse.getUser(); +// +// // acquire token for different account +// pca.acquireToken(UserNamePasswordParameters. +// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), +// adfsUser.getUpn(), +// adfsUser.getPassword().toCharArray()) +// .build()) +// .get(); +// +// assertEquals(pca.getAccounts().join().size(), 2); +// +// Set accounts = pca.getAccounts().join(); +// IAccount accountLabResponse1 = accounts.stream().filter( +// x -> x.username().equalsIgnoreCase( +// managedUser.getUpn())).findFirst().orElse(null); +// +// pca.removeAccount(accountLabResponse1).join(); +// +// assertEquals(pca.getAccounts().join().size(), 1); +// +// IAccount accountLabResponse2 = pca.getAccounts().get().iterator().next(); +// +// // Check that the right account was left in the cache +// assertEquals(accountLabResponse2.username(), adfsUser.getUpn()); +// } + + // TODO: labapi2 doesn't have guest user configuration yet - will be pulled from MSAL.NET +// @Test +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exception { +// +// UserQuery query = new UserQuery(); +// query.setUserType(LabServiceParameters.UserType.GUEST); +// +// LabResponse labResponse = LabUserHelper.getLabUserData(query); +// LabUser guestUser = labResponse.getUser(); +// Lab lab = labResponse.getLab(); +// +// String dataToInitCache = TestHelper.readResource( +// this.getClass(), +// "/cache_data/remove-account-test-cache.json"); +// +// // check that cache is empty +// assertEquals(dataToInitCache, ""); +// +// ITokenCacheAccessAspect persistenceAspect = new TokenPersistence(dataToInitCache); +// +// // acquire tokens for home tenant, and serialize cache +// PublicClientApplication pca = PublicClientApplication.builder( +// guestUser.getAppId()). +// authority(TestConstants.ORGANIZATIONS_AUTHORITY) +// .setTokenCacheAccessAspect(persistenceAspect) +// .build(); +// +// pca.acquireToken(UserNamePasswordParameters. +// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), +// guestUser.getHomeUPN(), +// guestUser.getPassword().toCharArray()) +// .build()) +// .get(); +// +// String guestTenantAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + lab.getTenantId(); +// +// // initialize pca with tenant where user is guest, deserialize cache, and acquire second token +// PublicClientApplication pca2 = PublicClientApplication.builder( +// guestUser.getAppId()). +// authority(guestTenantAuthority). +// setTokenCacheAccessAspect(persistenceAspect). +// build(); +// +// pca2.acquireToken(UserNamePasswordParameters. +// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), +// guestUser.getHomeUPN(), +// guestUser.getPassword().toCharArray()) +// .build()) +// .get(); +// +// // There should be two tokens in cache, with same accounts except for tenant +// assertEquals(pca2.getAccounts().join().iterator().next().getTenantProfiles().size(), 2); +// +// IAccount account = pca2.getAccounts().get().iterator().next(); +// +// // RemoveAccount should remove both cache entities +// pca2.removeAccount(account).join(); +// +// assertEquals(0, pca2.getAccounts().join().size()); +// +// //clean up file +// TestHelper.deleteFileContent( +// this.getClass(), +// "/cache_data/remove-account-test-cache.json"); +// } + + // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET +// @Test +// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") +// void retrieveAccounts_ADFSOnPrem() throws Exception { +// UserQuery query = new UserQuery(); +// query.setFederationProvider(LabServiceParameters.FederationProvider.ADFS_V2019); +// query.setUserType(LabServiceParameters.UserType.ON_PREM); +// +// LabResponse labResponse = LabUserHelper.getLabUserData(query); +// LabUser user = labResponse.getUser(); +// +// PublicClientApplication pca = PublicClientApplication.builder( +// TestConstants.ADFS_APP_ID). +// authority(TestConstants.ADFS_AUTHORITY). +// build(); +// +// pca.acquireToken(UserNamePasswordParameters. +// builder(Collections.singleton(TestConstants.ADFS_SCOPE), +// user.getUpn(), +// user.getPassword().toCharArray()) +// .build()) +// .get(); +// +// assertNotNull(pca.getAccounts().join().iterator().next()); +// assertEquals(pca.getAccounts().join().size(), 1); +// } private static class TokenPersistence implements ITokenCacheAccessAspect { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 074d0981..9e820707 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -23,7 +23,7 @@ class UsernamePasswordIT { void acquireTokenWithUsernamePassword_Managed(String environment) throws Exception { cfg = new Config(environment); - LabResponse labResponse = LabUserHelper.getDefaultUserAsync(environment).join(); + LabResponse labResponse = LabUserHelper.getDefaultUser(environment); assertAcquireTokenCommon(labResponse.getUser(), cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); } @@ -33,7 +33,7 @@ void acquireTokenWithUsernamePassword_Managed(String environment) throws Excepti void acquireTokenWithUsernamePassword_ADFSv2019_Federated(String environment) throws Exception { cfg = new Config(environment); - LabResponse labResponse = LabUserHelper.getDefaultAdfsUserAsync(environment).join(); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); @@ -42,7 +42,7 @@ void acquireTokenWithUsernamePassword_ADFSv2019_Federated(String environment) th @Test @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void acquireTokenWithUsernamePassword_ADFSv2019_OnPrem() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultAdfsUserAsync().join(); + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(); LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE, TestConstants.ADFS_APP_ID); @@ -50,7 +50,7 @@ void acquireTokenWithUsernamePassword_ADFSv2019_OnPrem() throws Exception { // @Test // void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { -// LabResponse labResponse = LabUserHelper.getDefaultUserAsync().join(); +// LabResponse labResponse = LabUserHelper.getDefaultUser(); // LabUser user = labResponse.getUser(); // // assertAcquireTokenCommon( @@ -68,7 +68,7 @@ void acquireTokenWithUsernamePassword_ADFSv2019_OnPrem() throws Exception { // query.setUserType(LabServiceParameters.UserType.CLOUD); // query.setAzureEnvironment(LabServiceParameters.AzureEnvironment.AZURE_CIAM); // -// LabResponse labResponse = LabUserHelper.getLabUserDataAsync(query).join(); +// LabResponse labResponse = LabUserHelper.getLabUserData(query); // // LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) // .authority("https://" + user.getLabName() + ".ciamlogin.com/") @@ -108,7 +108,7 @@ private void assertAcquireTokenCommon(LabUser user, String authority, String sco @Test void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Exception { - LabResponse labResponse = LabUserHelper.getB2CLocalAccountAsync().join(); + LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -138,7 +138,7 @@ void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Exception { @Test void acquireTokenWithUsernamePassword_B2C_LoginMicrosoftOnline() throws Exception { - LabResponse labResponse = LabUserHelper.getB2CLocalAccountAsync().join(); + LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java index ef8dd4ec..4efbc797 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java @@ -6,16 +6,26 @@ public class AppCredentialProvider { private String clientId; + private String oboClientId; + private String oboAppPassword; + private String oboAppIdURI; public AppCredentialProvider(String azureEnvironment) { + KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); switch (azureEnvironment) { case AzureEnvironment.AZURE: clientId = "c0485386-1e9a-4663-bc96-7ab30656de7f"; - + oboClientId = "f4aa5217-e87c-42b2-82af-5624dd14ee72"; + oboAppIdURI = "api://f4aa5217-e87c-42b2-82af-5624dd14ee72"; + oboAppPassword = keyVaultSecretsProvider.getSecretByName("TodoListServiceV2-OBO").getValue(); break; case AzureEnvironment.AZURE_US_GOVERNMENT: - clientId = LabApiConstants.ARLINGTON_APP_ID; + clientId = LabConstants.ARLINGTON_APP_ID; + oboClientId = LabConstants.ARLINGTON_OBO_APP_ID; + oboAppIdURI = "https://arlmsidlab1.us/IDLABS_APP_Confidential_Client"; + // TODO: Arlington OBO password needs to be retrieved from Key Vault or configured + oboAppPassword = null; break; default: throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); @@ -25,4 +35,16 @@ public AppCredentialProvider(String azureEnvironment) { public String getAppId() { return clientId; } + + public String getOboAppId() { + return oboClientId; + } + + public String getOboAppPassword() { + return oboAppPassword; + } + + public String getOboAppIdURI() { + return oboAppIdURI; + } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java index 8536fe0c..216ba91e 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java @@ -4,6 +4,7 @@ package com.microsoft.aad.msal4j.labapi2; public class Config { + private String commonAuthority; private String organizationsAuthority; private String graphDefaultScope; AppCredentialProvider appProvider; @@ -16,22 +17,28 @@ public Config(String azureEnvironment) { switch (azureEnvironment) { case AzureEnvironment.AZURE: - organizationsAuthority = LabApiConstants.ORGANIZATIONS_AUTHORITY; - graphDefaultScope = LabApiConstants.GRAPH_DEFAULT_SCOPE; + commonAuthority = LabConstants.COMMON_AUTHORITY; + organizationsAuthority = LabConstants.ORGANIZATIONS_AUTHORITY; + graphDefaultScope = LabConstants.GRAPH_DEFAULT_SCOPE; appProvider = new AppCredentialProvider(azureEnvironment); - tenant = LabApiConstants.MICROSOFT_AUTHORITY_TENANT; + tenant = LabConstants.MICROSOFT_AUTHORITY_TENANT; break; case AzureEnvironment.AZURE_US_GOVERNMENT: - organizationsAuthority = LabApiConstants.ARLINGTON_ORGANIZATIONS_AUTHORITY; - graphDefaultScope = LabApiConstants.ARLINGTON_GRAPH_DEFAULT_SCOPE; + commonAuthority = LabConstants.ARLINGTON_COMMON_AUTHORITY; + organizationsAuthority = LabConstants.ARLINGTON_ORGANIZATIONS_AUTHORITY; + graphDefaultScope = LabConstants.ARLINGTON_GRAPH_DEFAULT_SCOPE; appProvider = new AppCredentialProvider(azureEnvironment); - tenant = LabApiConstants.ARLINGTON_AUTHORITY_TENANT; + tenant = LabConstants.ARLINGTON_AUTHORITY_TENANT; break; default: throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); } } + public String commonAuthority() { + return this.commonAuthority; + } + public String organizationsAuthority() { return this.organizationsAuthority; } @@ -43,4 +50,15 @@ public String graphDefaultScope() { public String tenant() { return this.tenant; } + + public String tenantSpecificAuthority() { + switch (azureEnvironment) { + case AzureEnvironment.AZURE: + return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; + case AzureEnvironment.AZURE_US_GOVERNMENT: + return LabConstants.ARLINGTON_MICROSOFT_AUTHORITY_HOST + tenant; + default: + throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); + } + } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java similarity index 87% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java index aa0bb85a..fb9401d6 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApiConstants.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java @@ -6,7 +6,7 @@ /** * Constants for lab API endpoints and query parameters. */ -public class LabApiConstants { +public class LabConstants { // Base endpoints public static final String LAB_ENDPOINT = "https://msidlab.com/api/user"; @@ -28,14 +28,17 @@ public class LabApiConstants { public static final String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; public static final String ARLINGTON_AUTHORITY_TENANT = "arlmsidlab1.onmicrosoft.us"; + public static final String COMMON_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "common/"; public static final String ORGANIZATIONS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "organizations/"; public static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; + public static final String ARLINGTON_COMMON_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "common/"; public static final String ARLINGTON_ORGANIZATIONS_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "organizations/"; public static final String ARLINGTON_GRAPH_DEFAULT_SCOPE = "https://graph.microsoft.us/.default"; public static final String GRAPH_DEFAULT_SCOPE = "https://graph.windows.net/.default"; public static final String ARLINGTON_APP_ID = "cb7faed4-b8c0-49ee-b421-f5ed16894c83"; + public static final String ARLINGTON_OBO_APP_ID = "c0555d2d-02f2-4838-802e-3463422e571d"; public static final String LAB_API_SCOPE = "https://request.msidlab.com/.default"; } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java index 31c70623..96017cdc 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java @@ -14,14 +14,10 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.MalformedURLException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import static com.microsoft.aad.msal4j.labapi2.LabHttpHelper.buildUrl; /** * Wrapper for lab service API interactions. @@ -37,140 +33,142 @@ public class LabServiceApi { * Returns a test user account for use in testing. * * @param query Any and all parameters that the returned user should satisfy - * @return CompletableFuture containing LabResponse with user that matches the query + * @return LabResponse with user that matches the query */ - public CompletableFuture getLabResponseFromApiAsync(UserQuery query) { + public LabResponse getLabResponseFromApi(UserQuery query) { log.debug("Querying lab API for user with parameters: {}", query); - return runQueryAsync(query) - .thenCompose(result -> { - if (result == null || result.trim().isEmpty()) { - log.error("No lab user found for query"); - throw new LabUserNotFoundException(query, - "No lab user with specified parameters exists"); - } - log.debug("Lab API returned {} characters of data", result.length()); - return createLabResponseFromResultStringAsync(result); - }) - .whenComplete((response, error) -> { - if (error != null) { - log.error("Failed to get lab response: {}", error.getMessage()); - } else if (response != null && response.getUser() != null) { - log.info("Successfully retrieved lab user: {}", response.getUser().getUpn()); - } - }); + try { + String result = runQuery(query); + + if (result == null || result.trim().isEmpty()) { + log.error("No lab user found for query"); + throw new LabUserNotFoundException(query, + "No lab user with specified parameters exists"); + } + log.debug("Lab API returned {} characters of data", result.length()); + + LabResponse response = createLabResponseFromResultString(result); + + if (response != null && response.getUser() != null) { + log.info("Successfully retrieved lab user: {}", response.getUser().getUpn()); + } + + return response; + } catch (Exception e) { + log.error("Failed to get lab response: {}", e.getMessage()); + throw new RuntimeException("Failed to get lab response", e); + } } /** * Create a LabResponse object from JSON result string. * * @param result JSON string containing lab user array - * @return CompletableFuture containing populated LabResponse + * @return Populated LabResponse */ - CompletableFuture createLabResponseFromResultStringAsync(String result) { - return CompletableFuture.supplyAsync(() -> { - try { - log.debug("Parsing lab user JSON response"); - - List userResponses; - try (JsonReader jsonReader = JsonProviders.createReader(result)) { - userResponses = jsonReader.readArray(LabUser::fromJson); - } - - if (userResponses.isEmpty()) { - log.error("Lab API returned empty user array"); - throw new RuntimeException("Lab API returned empty user array"); - } - - LabUser user = userResponses.get(0); - log.debug("Parsed lab user: {}, fetching app and lab info", user.getUpn()); - - // Fetch app and lab info - String appEndpoint = LabApiConstants.LAB_APP_ENDPOINT + user.getAppId(); - log.debug("Fetching app info from: {}", appEndpoint); - String appResponse = getLabResponseAsync(appEndpoint).join(); - - List labApps; - try (JsonReader appReader = JsonProviders.createReader(appResponse)) { - labApps = appReader.readArray(LabApp::fromJson); - } - - if (labApps.isEmpty()) { - log.error("No lab app found for appId: {}", user.getAppId()); - throw new RuntimeException("No lab app found for appId: " + user.getAppId()); - } - log.debug("Successfully parsed lab app: {}", labApps.get(0).getAppId()); - - String labInfoEndpoint = LabApiConstants.LAB_INFO_ENDPOINT + user.getLabName(); - log.debug("Fetching lab info from: {}", labInfoEndpoint); - String labInfoResponse = getLabResponseAsync(labInfoEndpoint).join(); - - List labs; - try (JsonReader labReader = JsonProviders.createReader(labInfoResponse)) { - labs = labReader.readArray(Lab::fromJson); - } - - if (labs.isEmpty()) { - log.error("No lab info found for labName: {}", user.getLabName()); - throw new RuntimeException("No lab info found for labName: " + user.getLabName()); - } - log.debug("Successfully parsed lab info for: {}", user.getLabName()); - - LabResponse response = new LabResponse(); - response.setUser(user); - response.setApp(labApps.get(0)); - response.setLab(labs.get(0)); - - log.info("Successfully created LabResponse for user: {}", user.getUpn()); - return response; + LabResponse createLabResponseFromResultString(String result) { + try { + log.debug("Parsing lab user JSON response"); - } catch (Exception e) { - log.error("Failed to create LabResponse from API result: {}", e.getMessage(), e); - throw new RuntimeException("Failed to create LabResponse from API result", e); + List userResponses; + try (JsonReader jsonReader = JsonProviders.createReader(result)) { + userResponses = jsonReader.readArray(LabUser::fromJson); + } + + if (userResponses.isEmpty()) { + log.error("Lab API returned empty user array"); + throw new RuntimeException("Lab API returned empty user array"); + } + + LabUser user = userResponses.get(0); + log.debug("Parsed lab user: {}, fetching app and lab info", user.getUpn()); + + // Fetch app and lab info + String appEndpoint = LabConstants.LAB_APP_ENDPOINT + user.getAppId(); + log.debug("Fetching app info from: {}", appEndpoint); + String appResponse = getLabResponse(appEndpoint); + + List labApps; + try (JsonReader appReader = JsonProviders.createReader(appResponse)) { + labApps = appReader.readArray(LabApp::fromJson); + } + + if (labApps.isEmpty()) { + log.error("No lab app found for appId: {}", user.getAppId()); + throw new RuntimeException("No lab app found for appId: " + user.getAppId()); + } + log.debug("Successfully parsed lab app: {}", labApps.get(0).getAppId()); + + String labInfoEndpoint = LabConstants.LAB_INFO_ENDPOINT + user.getLabName(); + log.debug("Fetching lab info from: {}", labInfoEndpoint); + String labInfoResponse = getLabResponse(labInfoEndpoint); + + List labs; + try (JsonReader labReader = JsonProviders.createReader(labInfoResponse)) { + labs = labReader.readArray(Lab::fromJson); + } + + if (labs.isEmpty()) { + log.error("No lab info found for labName: {}", user.getLabName()); + throw new RuntimeException("No lab info found for labName: " + user.getLabName()); } - }); + log.debug("Successfully parsed lab info for: {}", user.getLabName()); + + LabResponse response = new LabResponse(); + response.setUser(user); + response.setApp(labApps.get(0)); + response.setLab(labs.get(0)); + + log.info("Successfully created LabResponse for user: {}", user.getUpn()); + return response; + + } catch (Exception e) { + log.error("Failed to create LabResponse from API result: {}", e.getMessage(), e); + throw new RuntimeException("Failed to create LabResponse from API result", e); + } } /** * Execute a query against the lab API. * * @param query UserQuery parameters - * @return CompletableFuture containing JSON response string + * @return JSON response string */ - private CompletableFuture runQueryAsync(UserQuery query) { + private String runQuery(UserQuery query) { Map queryDict = new HashMap<>(); // Building user query - required parameters set to defaults if not supplied queryDict.put( - LabApiConstants.MULTI_FACTOR_AUTHENTICATION, + LabConstants.MULTI_FACTOR_AUTHENTICATION, MFA.NONE.toString() ); queryDict.put( - LabApiConstants.PROTECTION_POLICY, + LabConstants.PROTECTION_POLICY, ProtectionPolicy.NONE.toString() ); if (query.getUserType() != null) { - queryDict.put(LabApiConstants.USER_TYPE, query.getUserType().toString()); + queryDict.put(LabConstants.USER_TYPE, query.getUserType().toString()); } if (query.getB2cIdentityProvider() != null) { - queryDict.put(LabApiConstants.B2C_PROVIDER, + queryDict.put(LabConstants.B2C_PROVIDER, query.getB2cIdentityProvider().toString()); } if (query.getFederationProvider() != null) { - queryDict.put(LabApiConstants.FEDERATION_PROVIDER, + queryDict.put(LabConstants.FEDERATION_PROVIDER, query.getFederationProvider().toString()); } if (query.getAzureEnvironment() != null) { - queryDict.put(LabApiConstants.AZURE_ENVIRONMENT, + queryDict.put(LabConstants.AZURE_ENVIRONMENT, query.getAzureEnvironment().toString()); } - return sendLabRequestAsync(LabApiConstants.LAB_ENDPOINT, queryDict); + return sendLabRequest(LabConstants.LAB_ENDPOINT, queryDict); } /** @@ -178,23 +176,21 @@ private CompletableFuture runQueryAsync(UserQuery query) { * * @param requestUrl Base URL for the request * @param queryDict Query parameters - * @return CompletableFuture containing response string + * @return Response string */ - private CompletableFuture sendLabRequestAsync(String requestUrl, Map queryDict) { - return CompletableFuture.supplyAsync(() -> { - try { - log.debug("Acquiring lab access token"); - String accessToken = getLabAccessToken(); - log.debug("Sending lab request to: {} with {} query parameters", requestUrl, queryDict.size()); - - String response = LabHttpHelper.sendRequestToLab(requestUrl, queryDict, accessToken); - log.debug("Lab request successful, received {} characters", response.length()); - return response; - } catch (IOException e) { - log.error("Failed to send lab request to {}: {}", requestUrl, e.getMessage()); - throw new RuntimeException("Failed to send lab request", e); - } - }); + private String sendLabRequest(String requestUrl, Map queryDict) { + try { + log.debug("Acquiring lab access token"); + String accessToken = getLabAccessToken(); + log.debug("Sending lab request to: {} with {} query parameters", requestUrl, queryDict.size()); + + String response = LabHttpHelper.sendRequestToLab(requestUrl, queryDict, accessToken); + log.debug("Lab request successful, received {} characters", response.length()); + return response; + } catch (IOException e) { + log.error("Failed to send lab request to {}: {}", requestUrl, e.getMessage()); + throw new RuntimeException("Failed to send lab request", e); + } } private void initLabApp() { @@ -209,7 +205,7 @@ private void initLabApp() { labApp = ConfidentialClientApplication.builder( appID, keyVaultSecretsProvider.getClientCredentialFromKeyStore()) - .authority(LabApiConstants.MICROSOFT_AUTHORITY) + .authority(LabConstants.MICROSOFT_AUTHORITY) .build(); log.debug("Lab app initialized successfully"); } catch (Exception e) { @@ -226,7 +222,7 @@ private String getLabAccessToken() { log.debug("Acquiring lab access token"); IAuthenticationResult result = labApp.acquireToken( ClientCredentialParameters - .builder(Collections.singleton(LabApiConstants.LAB_API_SCOPE)) + .builder(Collections.singleton(LabConstants.LAB_API_SCOPE)) .build() ).get(); @@ -243,20 +239,18 @@ private String getLabAccessToken() { * Execute HTTP GET request to lab API endpoint. * * @param address Full URL to request - * @return CompletableFuture containing response body as string + * @return Response body as string */ - CompletableFuture getLabResponseAsync(String address) { - return CompletableFuture.supplyAsync(() -> { - try { - log.debug("Getting lab response from: {}", address); - String accessToken = getLabAccessToken(); - String response = LabHttpHelper.sendRequestToLab(address, accessToken); - log.debug("Successfully retrieved lab response: {} characters", response.length()); - return response; - } catch (IOException e) { - log.error("Failed to get lab response from {}: {}", address, e.getMessage()); - throw new RuntimeException("Failed to get lab response from: " + address, e); - } - }); + String getLabResponse(String address) { + try { + log.debug("Getting lab response from: {}", address); + String accessToken = getLabAccessToken(); + String response = LabHttpHelper.sendRequestToLab(address, accessToken); + log.debug("Successfully retrieved lab response: {} characters", response.length()); + return response; + } catch (IOException e) { + log.error("Failed to get lab response from {}: {}", address, e.getMessage()); + throw new RuntimeException("Failed to get lab response from: " + address, e); + } } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java index f1b2fc63..bdb0695e 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java @@ -6,14 +6,18 @@ public class LabServiceParameters { public enum FederationProvider { + NONE, // No federation ADFS_V4, @Deprecated // ADFSv3 is out of support, do not use. The Arlington lab is federated to ADFSv3, so this value is needed ADFS_V3, ADFS_V2019, + CIAMCUD, // CIAM CUD } public enum B2CIdentityProvider { LOCAL, // Local B2C account + GOOGLE, // Google B2C provider + FACEBOOK, // Facebook B2C provider } public enum UserType { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java index beb8edfc..02fe4d0b 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java @@ -24,6 +24,7 @@ public class LabUser implements JsonSerializable { private String lastUpdatedDate; private String tenantId; private String password; + private String federationProvider; static LabUser fromJson(JsonReader jsonReader) throws IOException { LabUser user = new LabUser(); @@ -79,6 +80,9 @@ static LabUser fromJson(JsonReader jsonReader) throws IOException { case "tenantId": user.tenantId = reader.getString(); break; + case "federationProvider": + user.federationProvider = reader.getString(); + break; default: reader.skipChildren(); break; @@ -125,6 +129,26 @@ public String getLabName() { return this.labName; } + public String getB2cProvider() { + return this.b2cProvider; + } + + public String getUserType() { + return this.userType; + } + + public String getHomeDomain() { + return this.homeDomain; + } + + public String getHomeUPN() { + return this.homeUPN; + } + + public String getTenantId() { + return this.tenantId; + } + /** * Get the user's password, fetching from MSID Key Vault if necessary. * @@ -137,4 +161,22 @@ public String getPassword() { } return password; } + + /** + * Get the federation provider for this user. + * + * @return The federation provider string (e.g., "adfsv2019", "adfsv4", "none") + */ + public String getFederationProvider() { + return federationProvider != null ? federationProvider : "none"; + } + + /** + * Set the federation provider for this user. + * + * @param federationProvider The federation provider string + */ + public void setFederationProvider(String federationProvider) { + this.federationProvider = federationProvider; + } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index 26f89d40..715a4504 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -9,7 +9,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; public class LabUserHelper { @@ -36,29 +35,28 @@ public class LabUserHelper { * Get lab user data with caching support. * * @param query The UserQuery to search for - * @return CompletableFuture containing LabResponse + * @return LabResponse */ - public static CompletableFuture getLabUserDataAsync(UserQuery query) { + public static LabResponse getLabUserData(UserQuery query) { if (userCache.containsKey(query)) { LabResponse cached = userCache.get(query); log.debug("Lab cache hit: {}", cached.getUser() != null ? cached.getUser().getUpn() : "N/A"); - return CompletableFuture.completedFuture(cached); + return cached; } - return labService.getLabResponseFromApiAsync(query) - .thenApply(response -> { - if (response == null) { - log.error("No lab user found for query"); - throw new LabUserNotFoundException(query, "Found no users for the given query."); - } + LabResponse response = labService.getLabResponseFromApi(query); + + if (response == null) { + log.error("No lab user found for query"); + throw new LabUserNotFoundException(query, "Found no users for the given query."); + } - log.info("Lab API returned user: {}", - response.getUser() != null ? response.getUser().getUpn() : "N/A"); + log.info("Lab API returned user: {}", + response.getUser() != null ? response.getUser().getUpn() : "N/A"); - userCache.put(query, response); - return response; - }); + userCache.put(query, response); + return response; } /** @@ -66,49 +64,47 @@ public static CompletableFuture getLabUserDataAsync(UserQuery query * Uses the MSAL Team vault for configuration data. * * @param secret The Key Vault secret name - * @return CompletableFuture containing either LabResponse or String + * @return Either LabResponse or String */ - public static CompletableFuture getKVLabData(String secret) { - return CompletableFuture.supplyAsync(() -> { - try { - log.debug("Retrieving Key Vault secret: {}", secret); - // Use MSAL Team vault for configuration - KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsal.getSecretByName(secret); - String labData = keyVaultSecret.getValue(); - - if (labData == null || labData.isEmpty()) { - log.error("Key Vault secret '{}' is empty", secret); - throw new LabUserNotFoundException(new UserQuery(), - "Found no content for secret '" + secret + "' in Key Vault."); - } + private static Object getKVLabData(String secret) { + try { + log.debug("Retrieving Key Vault secret: {}", secret); + // Use MSAL Team vault for configuration + KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsal.getSecretByName(secret); + String labData = keyVaultSecret.getValue(); - // Check if the value is JSON - if (isValidJson(labData)) { - LabResponse response; - try (JsonReader jsonReader = JsonProviders.createReader(labData)) { - response = LabResponse.fromJson(jsonReader); - } + if (labData == null || labData.isEmpty()) { + log.error("Key Vault secret '{}' is empty", secret); + throw new LabUserNotFoundException(new UserQuery(), + "Found no content for secret '" + secret + "' in Key Vault."); + } - if (response == null) { - log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secret); - throw new LabUserNotFoundException(new UserQuery(), - "Failed to deserialize Key Vault secret '" + secret + "' to LabResponse."); - } + // Check if the value is JSON + if (isValidJson(labData)) { + LabResponse response; + try (JsonReader jsonReader = JsonProviders.createReader(labData)) { + response = LabResponse.fromJson(jsonReader); + } - log.debug("Retrieved LabResponse from Key Vault '{}': {}", secret, - response.getUser() != null ? response.getUser().getUpn() : - response.getApp() != null ? response.getApp().getAppId() : "Unknown"); - return response; - } else { - log.debug("Retrieved raw string from Key Vault '{}': {} characters", secret, labData.length()); - return labData; + if (response == null) { + log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secret); + throw new LabUserNotFoundException(new UserQuery(), + "Failed to deserialize Key Vault secret '" + secret + "' to LabResponse."); } - } catch (Exception e) { - log.error("Failed to retrieve Key Vault secret '{}': {}", secret, e.getMessage()); - throw new RuntimeException( - "Failed to retrieve or parse Key Vault secret '" + secret + "'", e); + + log.debug("Retrieved LabResponse from Key Vault '{}': {}", secret, + response.getUser() != null ? response.getUser().getUpn() : + response.getApp() != null ? response.getApp().getAppId() : "Unknown"); + return response; + } else { + log.debug("Retrieved raw string from Key Vault '{}': {} characters", secret, labData.length()); + return labData; } - }); + } catch (Exception e) { + log.error("Failed to retrieve Key Vault secret '{}': {}", secret, e.getMessage()); + throw new RuntimeException( + "Failed to retrieve or parse Key Vault secret '" + secret + "'", e); + } } /** @@ -172,7 +168,7 @@ public static LabResponse mergeKVLabData(String... secrets) { for (String secret : secrets) { - Object data = getKVLabData(secret).join(); + Object data = getKVLabData(secret); if (data instanceof LabResponse) { LabResponse response = (LabResponse) data; @@ -222,31 +218,29 @@ private static boolean isValidJson(String value) { } } - public static CompletableFuture getDefaultUserAsync() { - return CompletableFuture.completedFuture( - mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON")); + public static LabResponse getDefaultUser() { + return mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); } - public static CompletableFuture getDefaultAdfsUserAsync() { - return CompletableFuture.completedFuture( - mergeKVLabData("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON")); + public static LabResponse getDefaultAdfsUser() { + return mergeKVLabData("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); } - public static CompletableFuture getB2CLocalAccountAsync() { - return getLabUserDataAsync(UserQuery.b2cLocalAccountUserQuery()); + public static LabResponse getB2CLocalAccount() { + return getLabUserData(UserQuery.b2cLocalAccountUserQuery()); } - public static CompletableFuture getArlingtonUserAsync() { + public static LabResponse getArlingtonUser() { // Query the Lab API with Arlington-specific parameters - return getLabUserDataAsync(UserQuery.arlingtonUserQuery()); + return getLabUserData(UserQuery.arlingtonUserQuery()); } - public static CompletableFuture getArlingtonADFSUserAsync() { + public static LabResponse getArlingtonADFSUser() { // Create a modified query with federated user type UserQuery query = UserQuery.arlingtonUserQuery(); query.setUserType(LabServiceParameters.UserType.FEDERATED); - return getLabUserDataAsync(query); + return getLabUserData(query); } /** @@ -254,15 +248,15 @@ public static CompletableFuture getArlingtonADFSUserAsync() { * This is the primary helper method for parameterized tests that run across multiple clouds. * * @param azureEnvironment The Azure environment (e.g., AzureEnvironment.AZURE or AZURE_US_GOVERNMENT) - * @return CompletableFuture containing LabResponse for a managed user in that environment + * @return LabResponse for a managed user in that environment */ - public static CompletableFuture getDefaultUserAsync(String azureEnvironment) { + public static LabResponse getDefaultUser(String azureEnvironment) { log.debug("Getting default user for environment: {}", azureEnvironment); if (AzureEnvironment.AZURE.equals(azureEnvironment)) { - return getDefaultUserAsync(); + return getDefaultUser(); } else if (AzureEnvironment.AZURE_US_GOVERNMENT.equals(azureEnvironment)) { - return getArlingtonUserAsync(); + return getArlingtonUser(); } else { log.error("Unsupported Azure environment: {}", azureEnvironment); throw new IllegalArgumentException("Unsupported Azure environment: " + azureEnvironment); @@ -274,15 +268,15 @@ public static CompletableFuture getDefaultUserAsync(String azureEnv * Currently ADFS users are environment-agnostic and come from Key Vault. * * @param azureEnvironment The Azure environment (included for consistency, currently unused) - * @return CompletableFuture containing LabResponse for an ADFS federated user + * @return LabResponse for an ADFS federated user */ - public static CompletableFuture getDefaultAdfsUserAsync(String azureEnvironment) { + public static LabResponse getDefaultAdfsUser(String azureEnvironment) { log.debug("Getting default ADFS user for environment: {}", azureEnvironment); if (AzureEnvironment.AZURE.equals(azureEnvironment)) { - return getDefaultUserAsync(); + return getDefaultUser(); } else if (AzureEnvironment.AZURE_US_GOVERNMENT.equals(azureEnvironment)) { - return getArlingtonADFSUserAsync(); + return getArlingtonADFSUser(); } else { log.error("Unsupported Azure environment: {}", azureEnvironment); throw new IllegalArgumentException("Unsupported Azure environment: " + azureEnvironment); diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index 1eb04821..0d0b409b 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -4,7 +4,7 @@ package infrastructure; import com.microsoft.aad.msal4j.TestConstants; -import labapi.User; +import com.microsoft.aad.msal4j.labapi2.LabUser; import org.apache.commons.io.FileUtils; import org.openqa.selenium.By; import org.openqa.selenium.OutputType; @@ -70,7 +70,7 @@ public static WebElement waitForElementToBeVisibleAndEnable(WebDriver driver, By return waitForElementToBeVisibleAndEnable(driver, by, DEFAULT_TIMEOUT_IN_SEC); } - public static void performADOrCiamLogin(WebDriver driver, User user) { + public static void performADOrCiamLogin(WebDriver driver, LabUser user) { LOG.info("performADOrCiamLogin"); UserInformationFields fields = new UserInformationFields(user); @@ -134,7 +134,7 @@ private static void checkAuthenticationCompletePage(WebDriver driver) { }); } - public static void performADFS2019Login(WebDriver driver, User user) { + public static void performADFS2019Login(WebDriver driver, LabUser user) { LOG.info("PerformADFS2019Login"); UserInformationFields fields = new UserInformationFields(user); @@ -151,7 +151,7 @@ public static void performADFS2019Login(WebDriver driver, User user) { click(); } - public static void performLocalLogin(WebDriver driver, User user) { + public static void performLocalLogin(WebDriver driver, LabUser user) { LOG.info("PerformLocalLogin"); driver.findElement(new By.ById(SeleniumConstants.B2C_LOCAL_ACCOUNT_ID)).click(); @@ -167,7 +167,7 @@ public static void performLocalLogin(WebDriver driver, User user) { click(); } - public static void performGoogleLogin(WebDriver driver, User user) { + public static void performGoogleLogin(WebDriver driver, LabUser user) { LOG.info("PerformGoogleLogin"); driver.findElement(new By.ById(SeleniumConstants.GOOGLE_ACCOUNT_ID)).click(); @@ -187,7 +187,7 @@ public static void performGoogleLogin(WebDriver driver, User user) { waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.GOOGLE_NEXT_BUTTON_ID)).click(); } - public static void performFacebookLogin(WebDriver driver, User user) { + public static void performFacebookLogin(WebDriver driver, LabUser user) { LOG.info("PerformFacebookLogin"); driver.findElement(new By.ById(SeleniumConstants.FACEBOOK_ACCOUNT_ID)).click(); diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java index 73567824..dd826709 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java @@ -3,16 +3,14 @@ package infrastructure; -import labapi.FederationProvider; -import labapi.LabConstants; -import labapi.User; +import com.microsoft.aad.msal4j.labapi2.LabUser; class UserInformationFields { - private final User user; + private final LabUser user; private String passwordInputId; private String passwordSigInButtonId; - UserInformationFields(User labUser) { + UserInformationFields(LabUser labUser) { this.user = labUser; } @@ -43,18 +41,17 @@ String getADFS2019UserNameInputId() { } private void determineFieldIds() { - switch (user.getFederationProvider()) { - case FederationProvider.ADFS_2019: - passwordInputId = SeleniumConstants.ADFS2019_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.ADFS2019_SUBMIT_ID; - break; - case FederationProvider.ADFS_4: - passwordInputId = SeleniumConstants.ADFSV4_WEB_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.ADFSV4_WEB_SUBMIT_ID; - break; - default: - passwordInputId = SeleniumConstants.WEB_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.WEB_SUBMIT_ID; + String federationProvider = user.getFederationProvider(); + + if ("adfsv2019".equalsIgnoreCase(federationProvider)) { + passwordInputId = SeleniumConstants.ADFS2019_PASSWORD_ID; + passwordSigInButtonId = SeleniumConstants.ADFS2019_SUBMIT_ID; + } else if ("adfsv4".equalsIgnoreCase(federationProvider)) { + passwordInputId = SeleniumConstants.ADFSV4_WEB_PASSWORD_ID; + passwordSigInButtonId = SeleniumConstants.ADFSV4_WEB_SUBMIT_ID; + } else { + passwordInputId = SeleniumConstants.WEB_PASSWORD_ID; + passwordSigInButtonId = SeleniumConstants.WEB_SUBMIT_ID; } } } From 5ec7b2f22591cbde7104b67a250145963f6f2cb2 Mon Sep 17 00:00:00 2001 From: avdunn Date: Wed, 19 Nov 2025 10:06:36 -0800 Subject: [PATCH 03/14] Non-working, removed ADFS 2019 and unnecessary Arlington tests --- .../AcquireTokenInteractiveIT.java | 35 ++---- .../AcquireTokenSilentIT.java | 112 ++++++------------ .../AuthorizationCodeIT.java | 42 ++----- .../AzureEnvironmentIT.java | 19 ++- .../java/com.microsoft.aad.msal4j/Config.java | 63 ---------- .../DeviceCodeIT.java | 66 ++--------- .../EnvironmentsProvider.java | 14 --- .../HttpClientIT.java | 6 +- .../OnBehalfOfIT.java | 28 ++--- .../RefreshTokenIT.java | 13 +- .../TokenCacheIT.java | 32 +---- .../UsernamePasswordIT.java | 30 +---- .../msal4j/labapi2/AppCredentialProvider.java | 23 +--- .../microsoft/aad/msal4j/labapi2/Config.java | 42 ++----- .../aad/msal4j/labapi2/LabUserHelper.java | 40 ------- 15 files changed, 118 insertions(+), 447 deletions(-) delete mode 100644 msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/Config.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index 30f7cca3..7445eb80 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -6,6 +6,7 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,12 +38,11 @@ public void startBrowser() { startUpBrowser(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenInteractive_ManagedUser(String environment) { - cfg = new Config(environment); + @Test + void acquireTokenInteractive_ManagedUser() { + cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), cfg.commonAuthority(), cfg.graphDefaultScope()); } @@ -56,17 +56,6 @@ void acquireTokenInteractive_ManagedUser(String environment) { // assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE); // } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenInteractive_ADFSv2019_Federated(String environment) { - cfg = new Config(environment); - - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); - LabUser user = labResponse.getUser(); - assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), cfg.organizationsAuthority(), cfg.graphDefaultScope()); - } - // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET // @ParameterizedTest // @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") @@ -79,20 +68,18 @@ void acquireTokenInteractive_ADFSv2019_Federated(String environment) { // assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); // } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithAuthorizationCode_B2C_Local(String environment) { - cfg = new Config(environment); + @Test + void acquireTokenWithAuthorizationCode_B2C_Local() { + cfg = new Config(); LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithAuthorizationCode_B2C_LegacyFormat(String environment) { - cfg = new Config(environment); + @Test + void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { + cfg = new Config(); LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index 05c30735..3ab62306 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -24,11 +24,9 @@ class AcquireTokenSilentIT { private Config cfg; - - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_OrganizationAuthority_TokenRefreshed(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenSilent_OrganizationAuthority_TokenRefreshed() throws Exception { + cfg = new Config(); // When using common, organization, or consumer tenants, cache has no way // of determining which access token to return therefore token is always refreshed @@ -39,14 +37,12 @@ void acquireTokenSilent_OrganizationAuthority_TokenRefreshed(String environment) assertResultNotNull(result); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_LabAuthority_TokenNotRefreshed(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenSilent_LabAuthority_TokenNotRefreshed() throws Exception { + cfg = new Config(); // Access token should be returned from cache, and not using refresh token - - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -67,12 +63,11 @@ void acquireTokenSilent_LabAuthority_TokenNotRefreshed(String environment) throw assertEquals(TokenSource.CACHE, acquireSilentResult.metadata().tokenSource()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_ForceRefresh(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenSilent_ForceRefresh() throws Exception { + cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -119,34 +114,6 @@ void acquireTokenSilent_ForceRefresh(String environment) throws Exception { // assertEquals(result.account().username(), user.getUpn()); // } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenSilent_ADFS2019(String environment) throws Exception { - cfg = new Config(environment); - - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); - LabUser user = labResponse.getUser(); - - PublicClientApplication pca = PublicClientApplication.builder( - labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). - build(); - - IAuthenticationResult result = acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); - assertResultNotNull(result); - - IAccount account = pca.getAccounts().join().iterator().next(); - IAuthenticationResult acquireSilentResult = acquireTokenSilently(pca, account, TestConstants.ADFS_SCOPE, false); - assertResultNotNull(acquireSilentResult); - - account = pca.getAccounts().join().iterator().next(); - IAuthenticationResult resultAfterRefresh = acquireTokenSilently(pca, account, TestConstants.ADFS_SCOPE, true); - assertResultNotNull(resultAfterRefresh); - - assertTokensAreNotEqual(result, resultAfterRefresh); - } - @Test void acquireTokenSilent_usingCommonAuthority_returnCachedAt() throws Exception { acquireTokenSilent_returnCachedTokens(cfg.organizationsAuthority()); @@ -157,10 +124,9 @@ void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exc acquireTokenSilent_returnCachedTokens(cfg.tenantSpecificAuthority()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_ConfidentialClient_acquireTokenSilent(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenSilent_ConfidentialClient_acquireTokenSilent() throws Exception { + cfg = new Config(); IConfidentialClientApplication cca = getConfidentialClientApplications(); //test that adding extra query parameters does not break the flow @@ -190,7 +156,7 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilent(String environment @Test void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrowsException() throws Exception { - cfg = new Config(AzureEnvironment.AZURE); + cfg = new Config(); IConfidentialClientApplication cca = getConfidentialClientApplications(); @@ -209,12 +175,11 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrow .get()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_WithRefreshOn(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenSilent_WithRefreshOn() throws Exception { + cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -257,12 +222,11 @@ void acquireTokenSilent_WithRefreshOn(String environment) throws Exception { assertEquals(TokenSource.IDENTITY_PROVIDER, resultSilentWithRefreshOn.metadata().tokenSource()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_TenantAsParameter(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenSilent_TenantAsParameter() throws Exception { + cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -290,11 +254,10 @@ void acquireTokenSilent_TenantAsParameter(String environment) throws Exception { assertTokensAreNotEqual(result, resultWithTenantParam); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_emptyStringScope(String environment) throws Exception { - cfg = new Config(environment); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + @Test + void acquireTokenSilent_emptyStringScope() throws Exception { + cfg = new Config(); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -312,11 +275,10 @@ void acquireTokenSilent_emptyStringScope(String environment) throws Exception { assertEquals(result.accessToken(), silentResult.accessToken()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_emptyScopeSet(String environment) throws Exception { - cfg = new Config(environment); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + @Test + void acquireTokenSilent_emptyScopeSet() throws Exception { + cfg = new Config(); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); Set scopes = new HashSet<>(); @@ -345,8 +307,8 @@ void acquireTokenSilent_emptyScopeSet(String environment) throws Exception { @Test public void acquireTokenSilent_ClaimsForceRefresh() throws Exception { - cfg = new Config(AzureEnvironment.AZURE); - LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + cfg = new Config(); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); Set scopes = new HashSet<>(); @@ -387,8 +349,8 @@ public void acquireTokenSilent_ClaimsForceRefresh() throws Exception { } private IConfidentialClientApplication getConfidentialClientApplications() throws Exception { - String clientId = cfg.appProvider.getOboAppId(); - String password = cfg.appProvider.getOboAppPassword(); + String clientId = cfg.appProvider().getOboAppId(); + String password = cfg.appProvider().getOboAppPassword(); IClientCredential credential = ClientCredentialFactory.createFromSecret(password); @@ -400,7 +362,7 @@ private IConfidentialClientApplication getConfidentialClientApplications() throw } private void acquireTokenSilent_returnCachedTokens(String authority) throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( @@ -424,7 +386,7 @@ private void acquireTokenSilent_returnCachedTokens(String authority) throws Exce private IPublicClientApplication getPublicClientApplicationWithTokensInCache() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index c2b3fd19..67049658 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -42,34 +42,13 @@ public void startBrowser() { startUpBrowser(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - public void acquireTokenWithAuthorizationCode_ManagedUser(String environment) { - cfg = new Config(environment); + @Test + public void acquireTokenWithAuthorizationCode_ManagedUser() { + cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - assertAcquireTokenAAD(user, null); - } - - // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET -// @Test -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// public void acquireTokenWithAuthorizationCode_ADFSv2019_OnPrem() { -// LabResponse labResponse = LabUserHelper.getOnPremAdfsUser(LabServiceParameters.FederationProvider.ADFS_V2019); -// LabUser user = labResponse.getUser(); -// assertAcquireTokenADFS2019(user); -// } - - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - public void acquireTokenWithAuthorizationCode_ADFSv2019_Federated(String environment) { - cfg = new Config(environment); - - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); - LabUser user = labResponse.getUser(); - assertAcquireTokenAAD(user, null); + assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), null); } // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET @@ -85,10 +64,9 @@ public void acquireTokenWithAuthorizationCode_ADFSv2019_Federated(String environ // assertAcquireTokenAAD(user, null); // } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - public void acquireTokenWithAuthorizationCode_B2C_Local(String environment) { - cfg = new Config(environment); + @Test + public void acquireTokenWithAuthorizationCode_B2C_Local() { + cfg = new Config(); LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); @@ -155,9 +133,9 @@ private void assertAcquireTokenADFS2019(LabUser user) { assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenAAD(LabUser user, Map> parameters) { + private void assertAcquireTokenAAD(LabUser user, String appId, Map> parameters) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), cfg.commonAuthority()); + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, cfg.commonAuthority()); String authCode = acquireAuthorizationCodeAutomated(user, pca, parameters); IAuthenticationResult result = acquireTokenAuthorizationCodeFlow( diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java index d21ede70..8788661a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java @@ -21,30 +21,25 @@ class AzureEnvironmentIT { @Test void acquireTokenWithUsernamePassword_AzureGovernment() throws Exception { - assertAcquireTokenCommon(AzureEnvironment.AZURE_US_GOVERNMENT); - } - - private void assertAcquireTokenCommon(String azureEnvironment) throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(azureEnvironment); + LabResponse labResponse = LabUserHelper.getArlingtonUser(); LabUser user = labResponse.getUser(); LabApp app = labResponse.getApp(); PublicClientApplication pca = PublicClientApplication.builder( - app.getAppId()). + app.getAppId()). authority(app.getAuthority() + "organizations/"). build(); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters - .builder(Collections.singleton(TestConstants.USER_READ_SCOPE), - user.getUpn(), - user.getPassword().toCharArray()) - .build()) + .builder(Collections.singleton(TestConstants.USER_READ_SCOPE), + user.getUpn(), + user.getPassword().toCharArray()) + .build()) .get(); assertNotNull(result); assertNotNull(result.accessToken()); assertNotNull(result.idToken()); - assertEquals(user.getUpn(), result.account().username()); - } + assertEquals(user.getUpn(), result.account().username()); } } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/Config.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/Config.java deleted file mode 100644 index 883855fd..00000000 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/Config.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j; - -import labapi.AppCredentialProvider; -import labapi.AzureEnvironment; - -public class Config { - private String organizationsAuthority; - private String tenantSpecificAuthority; - private String commonAuthority; - private String graphDefaultScope; - AppCredentialProvider appProvider; - private String tenant; - - String azureEnvironment; - - Config(String azureEnvironment) { - this.azureEnvironment = azureEnvironment; - - switch (azureEnvironment) { - case AzureEnvironment.AZURE: - organizationsAuthority = TestConstants.ORGANIZATIONS_AUTHORITY; - commonAuthority = TestConstants.COMMON_AUTHORITY; - tenantSpecificAuthority = TestConstants.TENANT_SPECIFIC_AUTHORITY; - graphDefaultScope = TestConstants.GRAPH_DEFAULT_SCOPE; - appProvider = new AppCredentialProvider(azureEnvironment); - tenant = TestConstants.MICROSOFT_AUTHORITY_TENANT; - break; - case AzureEnvironment.AZURE_US_GOVERNMENT: - organizationsAuthority = TestConstants.ARLINGTON_ORGANIZATIONS_AUTHORITY; - tenantSpecificAuthority = TestConstants.ARLINGTON_TENANT_SPECIFIC_AUTHORITY; - commonAuthority = TestConstants.ARLINGTON_COMMON_AUTHORITY; - graphDefaultScope = TestConstants.ARLINGTON_GRAPH_DEFAULT_SCOPE; - appProvider = new AppCredentialProvider(azureEnvironment); - tenant = TestConstants.ARLINGTON_AUTHORITY_TENANT; - break; - default: - throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); - } - } - - public String organizationsAuthority() { - return this.organizationsAuthority; - } - - public String tenantSpecificAuthority() { - return this.tenantSpecificAuthority; - } - - public String commonAuthority() { - return this.commonAuthority; - } - - public String graphDefaultScope() { - return this.graphDefaultScope; - } - - public String tenant() { - return this.tenant; - } -} diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index fd387e69..c410cbd4 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -32,12 +32,11 @@ void setUp() { seleniumDriver = SeleniumExtensions.createDefaultWebDriver(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void DeviceCodeFlowADTest(String environment) throws Exception { - Config cfg = new Config(environment); + @Test + void DeviceCodeFlowADTest() throws Exception { + Config cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = IntegrationTestHelper.createPublicApp(labResponse.getApp().getAppId(), cfg.commonAuthority()); @@ -53,32 +52,6 @@ void DeviceCodeFlowADTest(String environment) throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET -// @Test() -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void DeviceCodeFlowADFSv2019Test() throws Exception { -// -// LabResponse labResponse = LabUserHelper.getOnPremAdfsUser(LabServiceParameters.FederationProvider.ADFS_V2019); -// LabUser user = labResponse.getUser(); -// -// PublicClientApplication pca = PublicClientApplication.builder( -// TestConstants.ADFS_APP_ID). -// authority(TestConstants.ADFS_AUTHORITY).validateAuthority(false). -// build(); -// -// Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { -// runAutomatedDeviceCodeFlow(deviceCode, user); -// }; -// -// IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters -// .builder(Collections.singleton(TestConstants.ADFS_SCOPE), -// deviceCodeConsumer) -// .build()) -// .get(); -// -// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); -// } - // TODO: labapi2 doesn't have MSA user configuration yet - will be pulled from MSAL.NET // NOTE: This test was also failing intermittently in the pipeline runs for the same commit, but always passed locally. //@Test() @@ -112,31 +85,13 @@ void DeviceCodeFlowADTest(String environment) throws Exception { // } private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { - boolean isRunningLocally = true;//!Strings.isNullOrEmpty( - //System.getenv(TestConstants.LOCAL_FLAG_ENV_VAR)); - - boolean isADFS2019 = user.getFederationProvider().equals("adfsv2019"); - LOG.info("Device code running locally: " + isRunningLocally); try { String deviceCodeFormId; String continueButtonId; - if (isRunningLocally) { - if (isADFS2019) { - deviceCodeFormId = "userCodeInput"; - continueButtonId = "confirmationButton"; - } else { - deviceCodeFormId = "otc"; - continueButtonId = "idSIButton9"; - } - } else { - deviceCodeFormId = "code"; - continueButtonId = "continueBtn"; - } + deviceCodeFormId = "otc"; + continueButtonId = "idSIButton9"; LOG.info("Loggin in ... Entering device code"); - if (isADFS2019) { - seleniumDriver.manage().deleteAllCookies(); - } seleniumDriver.navigate().to(deviceCode.verificationUri()); seleniumDriver.findElement(new By.ById(deviceCodeFormId)).sendKeys(deviceCode.userCode()); @@ -146,15 +101,8 @@ private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { new By.ById(continueButtonId)); continueBtn.click(); - if (isADFS2019) { - SeleniumExtensions.performADFS2019Login(seleniumDriver, user); - } else { - SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); - } + SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); } catch (Exception e) { - if (!isRunningLocally) { - SeleniumExtensions.takeScreenShot(seleniumDriver); - } LOG.error("Browser automation failed: {}", e.getMessage()); throw new RuntimeException("Browser automation failed: " + e.getMessage()); } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java deleted file mode 100644 index 1d63e442..00000000 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/EnvironmentsProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j; - -import labapi.AzureEnvironment; - -public class EnvironmentsProvider { - public static Object[][] createData() { - return new Object[][]{ - {AzureEnvironment.AZURE}}; - //{AzureEnvironment.AZURE_US_GOVERNMENT}};//Might be removed entirely under new system - } -} diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java index 398c1bd2..2b891b46 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java @@ -20,21 +20,21 @@ class HttpClientIT { @Test void acquireToken_okHttpClient() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new OkHttpClientAdapter()); } @Test void acquireToken_apacheHttpClient() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new ApacheHttpClientAdapter()); } @Test void acquireToken_readTimeout() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); //Set a 1ms read timeout, which will almost certainly occur before the service can respond diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java index 45743e65..92ef96ee 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java @@ -4,6 +4,7 @@ package com.microsoft.aad.msal4j; import com.microsoft.aad.msal4j.labapi2.*; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -18,14 +19,13 @@ class OnBehalfOfIT { private Config cfg; - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithOBO_Managed(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenWithOBO_Managed() throws Exception { + cfg = new Config(); String accessToken = this.getAccessToken(); - final String clientId = cfg.appProvider.getOboAppId(); - final String password = cfg.appProvider.getOboAppPassword(); + final String clientId = cfg.appProvider().getOboAppId(); + final String password = cfg.appProvider().getOboAppPassword(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). @@ -41,14 +41,12 @@ void acquireTokenWithOBO_Managed(String environment) throws Exception { assertResultNotNull(result); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithOBO_testCache(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenWithOBO_testCache() throws Exception { String accessToken = this.getAccessToken(); - final String clientId = cfg.appProvider.getOboAppId(); - final String password = cfg.appProvider.getOboAppPassword(); + final String clientId = cfg.appProvider().getOboAppId(); + final String password = cfg.appProvider().getOboAppPassword(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). @@ -129,11 +127,11 @@ private void assertResultNotNull(IAuthenticationResult result) { private String getAccessToken() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - String clientId = cfg.appProvider.getAppId(); - String apiReadScope = cfg.appProvider.getOboAppIdURI() + "/user_impersonation"; + String clientId = cfg.appProvider().getAppId(); + String apiReadScope = cfg.appProvider().getOboAppIdURI() + "/user_impersonation"; PublicClientApplication pca = PublicClientApplication.builder( clientId). authority(cfg.tenantSpecificAuthority()). diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java index 64f7af8a..abb8df0f 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java @@ -20,8 +20,8 @@ class RefreshTokenIT { private Config cfg; - private void setUp(String environment) throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); + private void setUp() throws Exception { + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); pca = PublicClientApplication.builder( @@ -39,12 +39,11 @@ private void setUp(String environment) throws Exception { refreshToken = result.refreshToken(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithRefreshToken(String environment) throws Exception { - cfg = new Config(environment); + @Test + void acquireTokenWithRefreshToken() throws Exception { + cfg = new Config(); - setUp(environment); + setUp(); IAuthenticationResult result = pca.acquireToken(RefreshTokenParameters .builder( diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index a8a900e0..92cf0ccf 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -18,11 +18,11 @@ class TokenCacheIT { @Test void singleAccountInCache_RemoveAccountTest() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); + LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); @@ -169,34 +169,6 @@ void singleAccountInCache_RemoveAccountTest() throws Exception { // "/cache_data/remove-account-test-cache.json"); // } - // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET -// @Test -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void retrieveAccounts_ADFSOnPrem() throws Exception { -// UserQuery query = new UserQuery(); -// query.setFederationProvider(LabServiceParameters.FederationProvider.ADFS_V2019); -// query.setUserType(LabServiceParameters.UserType.ON_PREM); -// -// LabResponse labResponse = LabUserHelper.getLabUserData(query); -// LabUser user = labResponse.getUser(); -// -// PublicClientApplication pca = PublicClientApplication.builder( -// TestConstants.ADFS_APP_ID). -// authority(TestConstants.ADFS_AUTHORITY). -// build(); -// -// pca.acquireToken(UserNamePasswordParameters. -// builder(Collections.singleton(TestConstants.ADFS_SCOPE), -// user.getUpn(), -// user.getPassword().toCharArray()) -// .build()) -// .get(); -// -// assertNotNull(pca.getAccounts().join().iterator().next()); -// assertEquals(pca.getAccounts().join().size(), 1); -// } - - private static class TokenPersistence implements ITokenCacheAccessAspect { String data; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 9e820707..4a8920f8 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -18,34 +18,12 @@ class UsernamePasswordIT { private Config cfg; - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithUsernamePassword_Managed(String environment) throws Exception { - cfg = new Config(environment); - - LabResponse labResponse = LabUserHelper.getDefaultUser(environment); - assertAcquireTokenCommon(labResponse.getUser(), cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); - } - - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenWithUsernamePassword_ADFSv2019_Federated(String environment) throws Exception { - cfg = new Config(environment); - - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(environment); - LabUser user = labResponse.getUser(); - - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); - } - @Test - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenWithUsernamePassword_ADFSv2019_OnPrem() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(); - LabUser user = labResponse.getUser(); + void acquireTokenWithUsernamePassword_Managed() throws Exception { + cfg = new Config(); - assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE, TestConstants.ADFS_APP_ID); + LabResponse labResponse = LabUserHelper.getDefaultUser(); + assertAcquireTokenCommon(labResponse.getUser(), cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); } // @Test diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java index 4efbc797..bf9c96c8 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java @@ -10,26 +10,13 @@ public class AppCredentialProvider { private String oboAppPassword; private String oboAppIdURI; - public AppCredentialProvider(String azureEnvironment) { + public AppCredentialProvider() { KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - switch (azureEnvironment) { - case AzureEnvironment.AZURE: - clientId = "c0485386-1e9a-4663-bc96-7ab30656de7f"; - oboClientId = "f4aa5217-e87c-42b2-82af-5624dd14ee72"; - oboAppIdURI = "api://f4aa5217-e87c-42b2-82af-5624dd14ee72"; - oboAppPassword = keyVaultSecretsProvider.getSecretByName("TodoListServiceV2-OBO").getValue(); - break; - case AzureEnvironment.AZURE_US_GOVERNMENT: - clientId = LabConstants.ARLINGTON_APP_ID; - oboClientId = LabConstants.ARLINGTON_OBO_APP_ID; - oboAppIdURI = "https://arlmsidlab1.us/IDLABS_APP_Confidential_Client"; - // TODO: Arlington OBO password needs to be retrieved from Key Vault or configured - oboAppPassword = null; - break; - default: - throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); - } + clientId = "c0485386-1e9a-4663-bc96-7ab30656de7f"; + oboClientId = "f4aa5217-e87c-42b2-82af-5624dd14ee72"; + oboAppIdURI = "api://f4aa5217-e87c-42b2-82af-5624dd14ee72"; + oboAppPassword = keyVaultSecretsProvider.getSecretByName("TodoListServiceV2-OBO").getValue(); } public String getAppId() { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java index 216ba91e..0bf294b8 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java @@ -7,32 +7,19 @@ public class Config { private String commonAuthority; private String organizationsAuthority; private String graphDefaultScope; - AppCredentialProvider appProvider; + private AppCredentialProvider appProvider; private String tenant; String azureEnvironment; - public Config(String azureEnvironment) { - this.azureEnvironment = azureEnvironment; + public Config() { + this.azureEnvironment = "azurecloud"; + commonAuthority = LabConstants.COMMON_AUTHORITY; + organizationsAuthority = LabConstants.ORGANIZATIONS_AUTHORITY; + graphDefaultScope = LabConstants.GRAPH_DEFAULT_SCOPE; + appProvider = new AppCredentialProvider(); + tenant = LabConstants.MICROSOFT_AUTHORITY_TENANT; - switch (azureEnvironment) { - case AzureEnvironment.AZURE: - commonAuthority = LabConstants.COMMON_AUTHORITY; - organizationsAuthority = LabConstants.ORGANIZATIONS_AUTHORITY; - graphDefaultScope = LabConstants.GRAPH_DEFAULT_SCOPE; - appProvider = new AppCredentialProvider(azureEnvironment); - tenant = LabConstants.MICROSOFT_AUTHORITY_TENANT; - break; - case AzureEnvironment.AZURE_US_GOVERNMENT: - commonAuthority = LabConstants.ARLINGTON_COMMON_AUTHORITY; - organizationsAuthority = LabConstants.ARLINGTON_ORGANIZATIONS_AUTHORITY; - graphDefaultScope = LabConstants.ARLINGTON_GRAPH_DEFAULT_SCOPE; - appProvider = new AppCredentialProvider(azureEnvironment); - tenant = LabConstants.ARLINGTON_AUTHORITY_TENANT; - break; - default: - throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); - } } public String commonAuthority() { @@ -52,13 +39,10 @@ public String tenant() { } public String tenantSpecificAuthority() { - switch (azureEnvironment) { - case AzureEnvironment.AZURE: - return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; - case AzureEnvironment.AZURE_US_GOVERNMENT: - return LabConstants.ARLINGTON_MICROSOFT_AUTHORITY_HOST + tenant; - default: - throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); - } + return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; + } + + public AppCredentialProvider appProvider() { + return appProvider; } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index 715a4504..ed86e63e 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -242,44 +242,4 @@ public static LabResponse getArlingtonADFSUser() { return getLabUserData(query); } - - /** - * Get a default managed user for the specified Azure environment. - * This is the primary helper method for parameterized tests that run across multiple clouds. - * - * @param azureEnvironment The Azure environment (e.g., AzureEnvironment.AZURE or AZURE_US_GOVERNMENT) - * @return LabResponse for a managed user in that environment - */ - public static LabResponse getDefaultUser(String azureEnvironment) { - log.debug("Getting default user for environment: {}", azureEnvironment); - - if (AzureEnvironment.AZURE.equals(azureEnvironment)) { - return getDefaultUser(); - } else if (AzureEnvironment.AZURE_US_GOVERNMENT.equals(azureEnvironment)) { - return getArlingtonUser(); - } else { - log.error("Unsupported Azure environment: {}", azureEnvironment); - throw new IllegalArgumentException("Unsupported Azure environment: " + azureEnvironment); - } - } - - /** - * Get a default ADFS user for the specified Azure environment. - * Currently ADFS users are environment-agnostic and come from Key Vault. - * - * @param azureEnvironment The Azure environment (included for consistency, currently unused) - * @return LabResponse for an ADFS federated user - */ - public static LabResponse getDefaultAdfsUser(String azureEnvironment) { - log.debug("Getting default ADFS user for environment: {}", azureEnvironment); - - if (AzureEnvironment.AZURE.equals(azureEnvironment)) { - return getDefaultUser(); - } else if (AzureEnvironment.AZURE_US_GOVERNMENT.equals(azureEnvironment)) { - return getArlingtonADFSUser(); - } else { - log.error("Unsupported Azure environment: {}", azureEnvironment); - throw new IllegalArgumentException("Unsupported Azure environment: " + azureEnvironment); - } - } } \ No newline at end of file From 40a6b06a0e07c700925837aab5b78c09cd12b2ba Mon Sep 17 00:00:00 2001 From: avdunn Date: Wed, 19 Nov 2025 13:57:52 -0800 Subject: [PATCH 04/14] Working OBO, CIAM, MSA, and device code tests --- .../AcquireTokenInteractiveIT.java | 95 +++++++++---------- .../AuthorizationCodeIT.java | 75 +++++++-------- .../DeviceCodeIT.java | 63 ++++++------ .../OnBehalfOfIT.java | 12 ++- .../msal4j/labapi2/AppCredentialProvider.java | 8 +- .../microsoft/aad/msal4j/labapi2/Config.java | 4 + .../aad/msal4j/labapi2/LabConstants.java | 1 + .../aad/msal4j/labapi2/LabServiceApi.java | 5 + .../msal4j/labapi2/LabServiceParameters.java | 13 ++- .../aad/msal4j/labapi2/LabUserHelper.java | 26 ++++- .../aad/msal4j/labapi2/UserQuery.java | 83 +++++++++++++++- 11 files changed, 246 insertions(+), 139 deletions(-) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index 7445eb80..c492dcae 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -7,12 +7,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -20,6 +17,8 @@ import java.net.URI; import java.net.URL; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ExecutionException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -96,51 +95,51 @@ void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { // assertAcquireTokenInstanceAware(user); // } - // TODO: labapi2 doesn't have CIAM CUD user configuration yet - will be pulled from MSAL.NET -// @Test -// void acquireTokenInteractive_Ciam() { -// LabResponse labResponse = LabUserHelper.getCiamCudUser(); -// LabUser user = labResponse.getUser(); -// -// Map extraQueryParameters = new HashMap<>(); -// -// PublicClientApplication pca; -// try { -// pca = PublicClientApplication.builder( -// user.getAppId()). -// authority("https://" + user.getLabName() + ".ciamlogin.com/") -// .build(); -// } catch (MalformedURLException ex) { -// throw new RuntimeException(ex.getMessage()); -// } -// -// IAuthenticationResult result; -// try { -// URI url = new URI("http://localhost:8080"); -// -// SystemBrowserOptions browserOptions = -// SystemBrowserOptions -// .builder() -// .openBrowserAction(new SeleniumOpenBrowserAction(user, pca)) -// .build(); -// -// InteractiveRequestParameters parameters = InteractiveRequestParameters -// .builder(url) -// .scopes(Collections.singleton(TestConstants.USER_READ_SCOPE)) -// .extraQueryParameters(extraQueryParameters) -// .systemBrowserOptions(browserOptions) -// .build(); -// -// result = pca.acquireToken(parameters).get(); -// -// } catch (Exception e) { -// LOG.error("Error acquiring token with authCode: {}", e.getMessage()); -// throw new RuntimeException("Error acquiring token with authCode: " + e.getMessage()); -// } -// -// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); -// assertEquals(user.getUpn(), result.account().username()); -// } + @Test + void acquireTokenInteractive_Ciam() { + LabResponse labResponse = LabUserHelper.getCiamCudUser(); + LabUser user = labResponse.getUser(); + LabApp app = labResponse.getApp(); + + Map extraQueryParameters = new HashMap<>(); + + PublicClientApplication pca; + try { + pca = PublicClientApplication.builder( + app.getAppId()). + authority(app.getAuthority()) + .build(); + } catch (MalformedURLException ex) { + throw new RuntimeException(ex.getMessage()); + } + + IAuthenticationResult result; + try { + URI url = new URI("http://localhost:8080"); + + SystemBrowserOptions browserOptions = + SystemBrowserOptions + .builder() + .openBrowserAction(new SeleniumOpenBrowserAction(user, pca)) + .build(); + + InteractiveRequestParameters parameters = InteractiveRequestParameters + .builder(url) + .scopes(Collections.singleton(TestConstants.USER_READ_SCOPE)) + .extraQueryParameters(extraQueryParameters) + .systemBrowserOptions(browserOptions) + .build(); + + result = pca.acquireToken(parameters).get(); + + } catch (Exception e) { + LOG.error("Error acquiring token with authCode: {}", e.getMessage()); + throw new RuntimeException("Error acquiring token with authCode: " + e.getMessage()); + } + + IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); + assertEquals(user.getUpn(), result.account().username()); + } private void assertAcquireTokenCommon(LabUser user, String appId, String authority, String scope) { PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, authority); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index 67049658..a446b35e 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -6,13 +6,10 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.MalformedURLException; @@ -73,44 +70,40 @@ public void acquireTokenWithAuthorizationCode_B2C_Local() { assertAcquireTokenB2C(user); } - // TODO: labapi2 doesn't have CIAM CUD user configuration yet - will be pulled from MSAL.NET -// @Test -// public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { -// String authorityCud = "https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/"; -// LabResponse labResponse = LabUserHelper.getCiamCudUser(); -// LabUser user = labResponse.getUser(); -// -// PublicClientApplication pca = PublicClientApplication.builder( -// user.getAppId()). -// oidcAuthority(authorityCud). -// build(); -// -// assertEquals("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/.well-known/openid-configuration", -// pca.authenticationAuthority.canonicalAuthorityUrl.toString()); -// assertEquals("https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/oauth2/v2.0/authorize", -// pca.authenticationAuthority.authorizationEndpoint); -// -// String authCode = acquireAuthorizationCodeAutomated(user, pca, null); -// -// IAuthenticationResult result = pca.acquireToken(AuthorizationCodeParameters -// .builder(authCode, -// new URI(TestConstants.LOCALHOST + httpListener.port())) -// .scopes(Collections.singleton("user.read")) -// .build()) -// .get(); -// -// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); -// assertEquals(user.getUpn(), result.account().username()); -// -// IAuthenticationResult resultSilent = pca.acquireTokenSilently(SilentParameters -// .builder(Collections.singleton("user.read"), result.account()) -// .build()) -// .get(); -// -// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); -// assertEquals(resultSilent.accessToken(), result.accessToken()); -// assertEquals(resultSilent.account().username(), result.account().username()); -// } + @Test + public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { + String authorityCud = "https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/"; + + LabResponse labResponse = LabUserHelper.getCiamCudUser(); + LabUser user = labResponse.getUser(); + LabApp app = labResponse.getApp(); + + PublicClientApplication pca = PublicClientApplication.builder( + app.getAppId()). + oidcAuthority(authorityCud). + build(); + + String authCode = acquireAuthorizationCodeAutomated(user, pca, null); + + IAuthenticationResult result = pca.acquireToken(AuthorizationCodeParameters + .builder(authCode, + new URI(TestConstants.LOCALHOST + httpListener.port())) + .scopes(Collections.singleton("user.read")) + .build()) + .get(); + + IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); + assertEquals(user.getUpn(), result.account().username()); + + IAuthenticationResult resultSilent = pca.acquireTokenSilently(SilentParameters + .builder(Collections.singleton("user.read"), result.account()) + .build()) + .get(); + + IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); + assertEquals(resultSilent.accessToken(), result.accessToken()); + assertEquals(resultSilent.account().username(), result.account().username()); + } private void assertAcquireTokenADFS2019(LabUser user) { PublicClientApplication pca; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index c410cbd4..2ae96218 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -12,9 +12,6 @@ import org.slf4j.LoggerFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -52,37 +49,37 @@ void DeviceCodeFlowADTest() throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - // TODO: labapi2 doesn't have MSA user configuration yet - will be pulled from MSAL.NET - // NOTE: This test was also failing intermittently in the pipeline runs for the same commit, but always passed locally. + //TODO: This test is failing intermittently in the pipeline runs for the same commit, but always passes locally. Disabling until we can investigate more. //@Test() -// void DeviceCodeFlowMSATest() throws Exception { -// -// LabResponse labResponse = LabUserHelper.getMSAUser(); -// LabUser user = labResponse.getUser(); -// -// PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.CONSUMERS_AUTHORITY); -// -// Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { -// runAutomatedDeviceCodeFlow(deviceCode, user); -// }; -// -// IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters -// .builder(Collections.singleton(""), -// deviceCodeConsumer) -// .build()) -// .get(); -// -// assertNotNull(result); -// assertNotNull(result.accessToken()); -// -// result = pca.acquireTokenSilently(SilentParameters. -// builder(Collections.singleton(""), result.account()). -// build()) -// .get(); -// -// assertNotNull(result); -// assertNotNull(result.accessToken()); -// } + void DeviceCodeFlowMSATest() throws Exception { + + LabResponse labResponse = LabUserHelper.getMSAUser(); + LabUser user = labResponse.getUser(); + LabApp app = labResponse.getApp(); + + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(app.getAppId(), TestConstants.CONSUMERS_AUTHORITY); + + Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { + runAutomatedDeviceCodeFlow(deviceCode, user); + }; + + IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters + .builder(Collections.singleton(""), + deviceCodeConsumer) + .build()) + .get(); + + assertNotNull(result); + assertNotNull(result.accessToken()); + + result = pca.acquireTokenSilently(SilentParameters. + builder(Collections.singleton(""), result.account()). + build()) + .get(); + + assertNotNull(result); + assertNotNull(result.accessToken()); + } private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java index 92ef96ee..e76363f2 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java @@ -29,7 +29,7 @@ void acquireTokenWithOBO_Managed() throws Exception { ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). - authority(cfg.tenantSpecificAuthority()). + authority(cfg.tenantSpecificAuthority("10c419d4-4a50-45b2-aa4e-919fb84df24f")). build(); IAuthenticationResult result = @@ -43,6 +43,8 @@ void acquireTokenWithOBO_Managed() throws Exception { @Test void acquireTokenWithOBO_testCache() throws Exception { + cfg = new Config(); + String accessToken = this.getAccessToken(); final String clientId = cfg.appProvider().getOboAppId(); @@ -50,7 +52,7 @@ void acquireTokenWithOBO_testCache() throws Exception { ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). - authority(cfg.tenantSpecificAuthority()). + authority(cfg.tenantSpecificAuthority("10c419d4-4a50-45b2-aa4e-919fb84df24f")). build(); IAuthenticationResult result1 = @@ -131,10 +133,10 @@ private String getAccessToken() throws Exception { LabUser user = labResponse.getUser(); String clientId = cfg.appProvider().getAppId(); - String apiReadScope = cfg.appProvider().getOboAppIdURI() + "/user_impersonation"; + String apiReadScope = cfg.appProvider().getOboAppIdURI() + "/access_as_user"; PublicClientApplication pca = PublicClientApplication.builder( - clientId). - authority(cfg.tenantSpecificAuthority()). + clientId). + authority("https://login.microsoftonline.com/organizations"). build(); IAuthenticationResult result = pca.acquireToken( diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java index bf9c96c8..4fbd49c4 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java @@ -13,10 +13,10 @@ public class AppCredentialProvider { public AppCredentialProvider() { KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - clientId = "c0485386-1e9a-4663-bc96-7ab30656de7f"; - oboClientId = "f4aa5217-e87c-42b2-82af-5624dd14ee72"; - oboAppIdURI = "api://f4aa5217-e87c-42b2-82af-5624dd14ee72"; - oboAppPassword = keyVaultSecretsProvider.getSecretByName("TodoListServiceV2-OBO").getValue(); + clientId = "54a2d933-8bf8-483b-a8f8-0a31924f3c1f"; + oboClientId = "23c64cd8-21e4-41dd-9756-ab9e2c23f58c"; + oboAppIdURI = "api://23c64cd8-21e4-41dd-9756-ab9e2c23f58c"; + oboAppPassword = keyVaultSecretsProvider.getSecretByName("IdentityDivisionDotNetOBOServiceSecret").getValue(); } public String getAppId() { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java index 0bf294b8..0559da64 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java @@ -42,6 +42,10 @@ public String tenantSpecificAuthority() { return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; } + public String tenantSpecificAuthority(String tenant) { + return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; + } + public AppCredentialProvider appProvider() { return appProvider; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java index fb9401d6..d8281769 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java @@ -21,6 +21,7 @@ public class LabConstants { public static final String HOME_DOMAIN = "homedomain"; public static final String B2C_PROVIDER = "b2cprovider"; public static final String FEDERATION_PROVIDER = "federationprovider"; + public static final String SIGN_IN_AUDIENCE = "SignInAudience"; public static final String AZURE_ENVIRONMENT = "azureenvironment"; public static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java index 96017cdc..37d0c4b9 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java @@ -163,6 +163,11 @@ private String runQuery(UserQuery query) { query.getFederationProvider().toString()); } + if (query.getSignInAudience() != null) { + queryDict.put(LabConstants.SIGN_IN_AUDIENCE, + query.getSignInAudience().toString()); + } + if (query.getAzureEnvironment() != null) { queryDict.put(LabConstants.AZURE_ENVIRONMENT, query.getAzureEnvironment().toString()); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java index bdb0695e..4442ac99 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java @@ -10,20 +10,22 @@ public enum FederationProvider { ADFS_V4, @Deprecated // ADFSv3 is out of support, do not use. The Arlington lab is federated to ADFSv3, so this value is needed ADFS_V3, - ADFS_V2019, + ADFS_2022, + CIAM, // CIAM CIAMCUD, // CIAM CUD } public enum B2CIdentityProvider { LOCAL, // Local B2C account - GOOGLE, // Google B2C provider - FACEBOOK, // Facebook B2C provider + MSA } public enum UserType { B2C, CLOUD, FEDERATED, + ONPREM, + MSA } public enum MFA { @@ -33,4 +35,9 @@ public enum MFA { public enum ProtectionPolicy { NONE, } + + public enum SignInAudience + { + AzureAdMyOrg + } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index ed86e63e..c734ab0e 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -227,7 +227,7 @@ public static LabResponse getDefaultAdfsUser() { } public static LabResponse getB2CLocalAccount() { - return getLabUserData(UserQuery.b2cLocalAccountUserQuery()); + return getLabUserData(UserQuery.b2cLocalAccountQuery()); } public static LabResponse getArlingtonUser() { @@ -242,4 +242,28 @@ public static LabResponse getArlingtonADFSUser() { return getLabUserData(query); } + + public static LabResponse getCiamUser() { + return getLabUserData(UserQuery.ciamUserQuery()); + } + + public static LabResponse getCiamCudUser() { + return getLabUserData(UserQuery.ciamCudUserQuery()); + } + + public static LabResponse getMSAUser() { + return getLabUserData(UserQuery.msaUserQuery()); + } + + public static LabResponse getB2CMsaAccount() { + return getLabUserData(UserQuery.b2cMsaAccountQuery()); + } + + public static LabResponse getAdfs2022User() { + return getLabUserData(UserQuery.adfs2022UserQuery()); + } + + public static LabResponse getCiamOboUser() { + return getLabUserData(UserQuery.ciamOboUserQuery()); + } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java index f9459955..75b437d6 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java @@ -9,6 +9,7 @@ public class UserQuery { private UserType userType; private B2CIdentityProvider b2cIdentityProvider; private FederationProvider federationProvider; + private SignInAudience signInAudience; private String azureEnvironment; // Getters and Setters @@ -30,17 +31,91 @@ public void setAzureEnvironment(String azureEnvironment) { this.azureEnvironment = azureEnvironment; } - public static UserQuery b2cLocalAccountUserQuery() { + public SignInAudience getSignInAudience() { return signInAudience; } + public void setSignInAudience(SignInAudience signInAudience) { + this.signInAudience = signInAudience; + } + + public static UserQuery arlingtonUserQuery() { + UserQuery query = new UserQuery(); + query.setUserType(UserType.CLOUD); + query.setAzureEnvironment(AzureEnvironment.AZURE_US_GOVERNMENT); + return query; + } + + /** + * Gets an ADFS 2022 federated user from the lab. + * Uses the Lab API to query for a user with ADFS 2022 federation. + */ + public static UserQuery adfs2022UserQuery() { + UserQuery query = new UserQuery(); + query.setAzureEnvironment(AzureEnvironment.AZURE); + query.setFederationProvider(FederationProvider.ADFS_2022); + query.setUserType(UserType.FEDERATED); + return query; + } + + /** + * Gets a B2C local account (username/password) from the lab. + */ + public static UserQuery b2cLocalAccountQuery() { UserQuery query = new UserQuery(); query.setUserType(UserType.B2C); query.setB2cIdentityProvider(B2CIdentityProvider.LOCAL); return query; } - public static UserQuery arlingtonUserQuery() { + /** + * Gets a B2C user that authenticates with Microsoft Account (MSA). + */ + public static UserQuery b2cMsaAccountQuery() { UserQuery query = new UserQuery(); - query.setUserType(UserType.CLOUD); - query.setAzureEnvironment(AzureEnvironment.AZURE_US_GOVERNMENT); + query.setUserType(UserType.B2C); + query.setB2cIdentityProvider(B2CIdentityProvider.MSA); + return query; + } + + /** + * Gets a CIAM user with standard domain (tenant.ciamlogin.com). + * Uses the Lab API to query for CIAM users. + */ + public static UserQuery ciamUserQuery() { + UserQuery query = new UserQuery(); + query.setFederationProvider(FederationProvider.CIAM); + query.setSignInAudience(SignInAudience.AzureAdMyOrg); + return query; + } + + /** + * Gets a CIAM user with Custom User Domain (CUD). + * Example: login.customdomain.com instead of tenant.ciamlogin.com + */ + public static UserQuery ciamCudUserQuery() { + UserQuery query = new UserQuery(); + query.setFederationProvider(FederationProvider.CIAMCUD); + query.setSignInAudience(SignInAudience.AzureAdMyOrg); + return query; + } + + /** + * Gets a regular Microsoft Account (MSA) user, not tied to B2C. + * This is for consumer accounts like outlook.com, hotmail.com, etc. + */ + public static UserQuery msaUserQuery() { + UserQuery query = new UserQuery(); + query.setUserType(UserType.MSA); + return query; + } + + /** + * Gets a CIAM user for OBO scenarios. + * CIAM supports OBO flows, so this query gets a CIAM user that can be used + * in OBO tests. + */ + public static UserQuery ciamOboUserQuery() { + UserQuery query = new UserQuery(); + query.setFederationProvider(FederationProvider.CIAM); + query.setSignInAudience(SignInAudience.AzureAdMyOrg); return query; } From 0e87e7ee564056190e6609cfc1fc3e203717f020 Mon Sep 17 00:00:00 2001 From: avdunn Date: Thu, 20 Nov 2025 13:22:41 -0800 Subject: [PATCH 05/14] ADFS cleanup --- .../AcquireTokenInteractiveIT.java | 51 +++++++---------- .../AcquireTokenSilentIT.java | 26 --------- .../AuthorizationCodeIT.java | 30 +++++----- .../AzureEnvironmentIT.java | 6 -- .../SeleniumTest.java | 15 +---- .../TokenCacheIT.java | 53 ------------------ .../UsernamePasswordIT.java | 2 - .../msal4j/labapi2/LabServiceParameters.java | 2 +- .../aad/msal4j/labapi2/LabUserHelper.java | 4 -- .../aad/msal4j/labapi2/UserQuery.java | 12 ---- .../infrastructure/SeleniumConstants.java | 24 ++------ .../infrastructure/SeleniumExtensions.java | 56 ++----------------- .../infrastructure/UserInformationFields.java | 13 ++--- 13 files changed, 51 insertions(+), 243 deletions(-) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index c492dcae..a49ab7cb 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.jupiter.api.TestInstance; @@ -46,26 +47,15 @@ void acquireTokenInteractive_ManagedUser() { assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), cfg.commonAuthority(), cfg.graphDefaultScope()); } - // TODO: labapi2 doesn't have on-prem ADFS user configuration yet - will be pulled from MSAL.NET -// @Test() -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void acquireTokenInteractive_ADFSv2019_OnPrem() { -// LabResponse labResponse = LabUserHelper.getOnPremAdfsUser(LabServiceParameters.FederationProvider.ADFS_V2019); -// LabUser user = labResponse.getUser(); -// assertAcquireTokenCommon(user, TestConstants.ADFS_AUTHORITY, TestConstants.ADFS_SCOPE); -// } - - // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET -// @ParameterizedTest -// @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void acquireTokenInteractive_ADFSv4_Federated(String environment) { -// cfg = new Config(environment); -// -// LabResponse labResponse = LabUserHelper.getFederatedAdfsUser(environment, LabServiceParameters.FederationProvider.ADFS_V4); -// LabUser user = labResponse.getUser(); -// assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); -// } + //TODO: need to sort out ADFS 2022 configuration + @Test() + @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") + void acquireTokenInteractive_ADFSv2022() { + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(); + + LabUser user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority() + "organizations/", TestConstants.ADFS_SCOPE); + } @Test void acquireTokenWithAuthorizationCode_B2C_Local() { @@ -85,15 +75,14 @@ void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT); } - // TODO: labapi2 needs cross-cloud instance aware test configuration - will be pulled from MSAL.NET -// @Test -// void acquireTokenInteractive_ManagedUser_InstanceAware() { -// cfg = new Config(AzureEnvironment.AZURE); -// -// LabResponse labResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE_US_GOVERNMENT); -// LabUser user = labResponse.getUser(); -// assertAcquireTokenInstanceAware(user); -// } + @Test + void acquireTokenInteractive_ManagedUser_InstanceAware() { + cfg = new Config(); + + LabResponse labResponse = LabUserHelper.getArlingtonUser(); + LabUser user = labResponse.getUser(); + assertAcquireTokenInstanceAware(user, labResponse.getApp().getAppId(), labResponse.getLab().getTenantId()); + } @Test void acquireTokenInteractive_Ciam() { @@ -169,8 +158,8 @@ private void assertAcquireTokenB2C(LabUser user, String authority) { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - private void assertAcquireTokenInstanceAware(LabUser user) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + user.getTenantId()); + private void assertAcquireTokenInstanceAware(LabUser user, String appId, String tenantId) { + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, TestConstants.MICROSOFT_AUTHORITY_HOST + tenantId); IAuthenticationResult result = acquireTokenInteractive_instanceAware(user, pca, cfg.graphDefaultScope()); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index 3ab62306..ca9857a9 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -88,32 +88,6 @@ void acquireTokenSilent_ForceRefresh() throws Exception { assertEquals(TokenSource.IDENTITY_PROVIDER, resultAfterRefresh.metadata().tokenSource()); } - // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET -// @ParameterizedTest -// @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void acquireTokenSilent_MultipleAccountsInCache_UseCorrectAccount(String environment) throws Exception { -// cfg = new Config(environment); -// -// IPublicClientApplication pca = getPublicClientApplicationWithTokensInCache(); -// -// // get lab user for different account -// LabResponse labResponse = LabUserHelper.getFederatedAdfsUser(environment, LabServiceParameters.FederationProvider.ADFS_V4); -// LabUser user = labResponse.getUser(); -// -// // acquire token for different account -// acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); -// -// Set accounts = pca.getAccounts().join(); -// IAccount account = accounts.stream().filter( -// x -> x.username().equalsIgnoreCase( -// user.getUpn())).findFirst().orElse(null); -// -// IAuthenticationResult result = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), false); -// assertResultNotNull(result); -// assertEquals(result.account().username(), user.getUpn()); -// } - @Test void acquireTokenSilent_usingCommonAuthority_returnCachedAt() throws Exception { acquireTokenSilent_returnCachedTokens(cfg.organizationsAuthority()); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index a446b35e..ffa9047e 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -6,6 +6,7 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.jupiter.api.Test; @@ -48,19 +49,6 @@ public void acquireTokenWithAuthorizationCode_ManagedUser() { assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), null); } - // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET -// @ParameterizedTest -// @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// public void acquireTokenWithAuthorizationCode_ADFSv4_Federated(String environment) { -// cfg = new Config(environment); -// -// LabResponse labResponse = LabUserHelper.getFederatedAdfsUser(environment, LabServiceParameters.FederationProvider.ADFS_V4); -// LabUser user = labResponse.getUser(); -// -// assertAcquireTokenAAD(user, null); -// } - @Test public void acquireTokenWithAuthorizationCode_B2C_Local() { cfg = new Config(); @@ -105,12 +93,22 @@ public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { assertEquals(resultSilent.account().username(), result.account().username()); } - private void assertAcquireTokenADFS2019(LabUser user) { + //TODO: need to sort out ADFS 2022 configuration + @Test + @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") + void acquireTokenWithAuthorizationCode_ADFSv2022() { + LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(); + + LabUser user = labResponse.getUser(); + assertAcquireTokenADFS(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority() + "organizations/"); + } + + private void assertAcquireTokenADFS(LabUser user, String appId, String authority) { PublicClientApplication pca; try { pca = PublicClientApplication.builder( - TestConstants.ADFS_APP_ID). - authority(TestConstants.ADFS_AUTHORITY). + appId). + authority(authority). build(); } catch (MalformedURLException ex) { throw new RuntimeException(ex.getMessage()); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java index 8788661a..1f28030e 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java @@ -13,12 +13,6 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AzureEnvironmentIT { - // TODO: labapi2 doesn't have Azure China user configuration yet - will be pulled from MSAL.NET -// @Test -// void acquireTokenWithUsernamePassword_AzureChina() throws Exception { -// assertAcquireTokenCommon(AzureEnvironment.AZURE_CHINA); -// } - @Test void acquireTokenWithUsernamePassword_AzureGovernment() throws Exception { LabResponse labResponse = LabUserHelper.getArlingtonUser(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java index 50c9788a..4f3f507b 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java @@ -25,22 +25,13 @@ public void startUpBrowser() { void runSeleniumAutomatedLogin(LabUser user, AbstractClientApplicationBase app) { AuthorityType authorityType = app.authenticationAuthority.authorityType; + if (authorityType == AuthorityType.B2C) { - switch (user.getB2cProvider().toLowerCase()) { - case "local": - SeleniumExtensions.performLocalLogin(seleniumDriver, user); - break; - case "google": - SeleniumExtensions.performGoogleLogin(seleniumDriver, user); - break; - case "facebook": - SeleniumExtensions.performFacebookLogin(seleniumDriver, user); - break; - } + SeleniumExtensions.performLocalLogin(seleniumDriver, user); } else if (authorityType == AuthorityType.AAD) { SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); } else if (authorityType == AuthorityType.ADFS) { - SeleniumExtensions.performADFS2019Login(seleniumDriver, user); + SeleniumExtensions.performADFSLogin(seleniumDriver, user); } else if (authorityType == AuthorityType.CIAM || authorityType == AuthorityType.OIDC) { SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index 92cf0ccf..f8fc9536 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -49,59 +49,6 @@ void singleAccountInCache_RemoveAccountTest() throws Exception { assertEquals(pca.getAccounts().join().size(), 0); } - // TODO: labapi2 doesn't have ADFS v4 specific user helper yet - will be pulled from MSAL.NET -// @Test -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void twoAccountsInCache_RemoveAccountTest() throws Exception { -// -// LabResponse managedResponse = LabUserHelper.getDefaultUser(AzureEnvironment.AZURE); -// LabUser managedUser = managedResponse.getUser(); -// -// PublicClientApplication pca = PublicClientApplication.builder( -// managedResponse.getApp().getAppId()). -// authority(TestConstants.ORGANIZATIONS_AUTHORITY). -// build(); -// -// assertEquals(pca.getAccounts().join().size(), 0); -// -// pca.acquireToken(UserNamePasswordParameters. -// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), -// managedUser.getUpn(), -// managedUser.getPassword().toCharArray()) -// .build()) -// .get(); -// -// assertEquals(pca.getAccounts().join().size(), 1); -// -// // get lab user for different account -// LabResponse adfsResponse = LabUserHelper.getFederatedAdfsUser(AzureEnvironment.AZURE, LabServiceParameters.FederationProvider.ADFS_V4); -// LabUser adfsUser = adfsResponse.getUser(); -// -// // acquire token for different account -// pca.acquireToken(UserNamePasswordParameters. -// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), -// adfsUser.getUpn(), -// adfsUser.getPassword().toCharArray()) -// .build()) -// .get(); -// -// assertEquals(pca.getAccounts().join().size(), 2); -// -// Set accounts = pca.getAccounts().join(); -// IAccount accountLabResponse1 = accounts.stream().filter( -// x -> x.username().equalsIgnoreCase( -// managedUser.getUpn())).findFirst().orElse(null); -// -// pca.removeAccount(accountLabResponse1).join(); -// -// assertEquals(pca.getAccounts().join().size(), 1); -// -// IAccount accountLabResponse2 = pca.getAccounts().get().iterator().next(); -// -// // Check that the right account was left in the cache -// assertEquals(accountLabResponse2.username(), adfsUser.getUpn()); -// } - // TODO: labapi2 doesn't have guest user configuration yet - will be pulled from MSAL.NET // @Test // @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 4a8920f8..d88db5cc 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -71,8 +71,6 @@ private void assertAcquireTokenCommon(LabUser user, String authority, String sco authority(authority). build(); - System.out.println("Scope: " + scope); - System.out.println("UPN: " + user.getUpn()); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. builder(Collections.singleton(scope), user.getUpn(), diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java index 4442ac99..d5e4a6d7 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java @@ -10,7 +10,7 @@ public enum FederationProvider { ADFS_V4, @Deprecated // ADFSv3 is out of support, do not use. The Arlington lab is federated to ADFSv3, so this value is needed ADFS_V3, - ADFS_2022, + ADFSv2022, CIAM, // CIAM CIAMCUD, // CIAM CUD } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index c734ab0e..17fce975 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -259,10 +259,6 @@ public static LabResponse getB2CMsaAccount() { return getLabUserData(UserQuery.b2cMsaAccountQuery()); } - public static LabResponse getAdfs2022User() { - return getLabUserData(UserQuery.adfs2022UserQuery()); - } - public static LabResponse getCiamOboUser() { return getLabUserData(UserQuery.ciamOboUserQuery()); } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java index 75b437d6..f49ecfc3 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java @@ -43,18 +43,6 @@ public static UserQuery arlingtonUserQuery() { return query; } - /** - * Gets an ADFS 2022 federated user from the lab. - * Uses the Lab API to query for a user with ADFS 2022 federation. - */ - public static UserQuery adfs2022UserQuery() { - UserQuery query = new UserQuery(); - query.setAzureEnvironment(AzureEnvironment.AZURE); - query.setFederationProvider(FederationProvider.ADFS_2022); - query.setUserType(UserType.FEDERATED); - return query; - } - /** * Gets a B2C local account (username/password) from the lab. */ diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java index 99a461d0..5e266911 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java @@ -8,31 +8,15 @@ package infrastructure; public class SeleniumConstants { - //ADFS v4 - static final String ADFSV4_WEB_PASSWORD_ID = "passwordInput"; - static final String ADFSV4_WEB_SUBMIT_ID = "submitButton"; static final String WEB_UPN_INPUT_ID = "i0116"; static final String WEB_PASSWORD_ID = "i0118"; static final String WEB_SUBMIT_ID = "idSIButton9"; - //ADFS2019 - static final String ADFS2019_UPN_INPUT_ID = "userNameInput"; - static final String ADFS2019_PASSWORD_ID = "passwordInput"; - static final String ADFS2019_SUBMIT_ID = "submitButton"; - - //B2C Facebook - static final String FACEBOOK_ACCOUNT_ID = "FacebookExchange"; - static final String FACEBOOK_USERNAME_ID = "email"; - static final String FACEBOOK_PASSWORD_ID = "pass"; - static final String FACEBOOK_LOGIN_BUTTON_ID = "loginbutton"; - - //B2C Google - static final String GOOGLE_ACCOUNT_ID = "GoogleExchange"; - static final String GOOGLE_USERNAME_ID = "identifierId"; - static final String GOOGLE_NEXT_AFTER_USERNAME_BUTTON = "identifierNext"; - static final String GOOGLE_PASSWORD_ID = "password"; - static final String GOOGLE_NEXT_BUTTON_ID = "passwordNext"; + //ADFS + static final String ADFS_UPN_INPUT_ID = "userNameInput"; + static final String ADFS_PASSWORD_ID = "passwordInput"; + static final String ADFS_SUBMIT_ID = "submitButton"; // B2C Local static final String B2C_LOCAL_ACCOUNT_ID = "SignInWithLogonNameExchange"; diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index 0d0b409b..c51e94d9 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -38,7 +38,7 @@ public static WebDriver createDefaultWebDriver() { ChromeOptions options = new ChromeOptions(); //No visual rendering, remove to see browser window when debugging - options.addArguments("--headless"); +// options.addArguments("--headless"); //Add to avoid issues if your real browser's history/cookies are affecting tests, should not be needed in ADO pipelines options.addArguments("--incognito"); @@ -134,13 +134,13 @@ private static void checkAuthenticationCompletePage(WebDriver driver) { }); } - public static void performADFS2019Login(WebDriver driver, LabUser user) { - LOG.info("PerformADFS2019Login"); + public static void performADFSLogin(WebDriver driver, LabUser user) { + LOG.info("PerformADFSLogin"); UserInformationFields fields = new UserInformationFields(user); LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(fields.getADFS2019UserNameInputId())).sendKeys(user.getUpn()); + driver.findElement(new By.ById(fields.getADFSUserNameInputId())).sendKeys(user.getUpn()); LOG.info("Loggin in ... Entering password"); By by = new By.ById(fields.getPasswordInputId()); @@ -166,52 +166,4 @@ public static void performLocalLogin(WebDriver driver, LabUser user) { waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.B2C_LOCAL_SIGN_IN_BUTTON_ID)). click(); } - - public static void performGoogleLogin(WebDriver driver, LabUser user) { - LOG.info("PerformGoogleLogin"); - - driver.findElement(new By.ById(SeleniumConstants.GOOGLE_ACCOUNT_ID)).click(); - - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(SeleniumConstants.GOOGLE_USERNAME_ID)).sendKeys(user.getUpn()); - - LOG.info("Loggin in ... Clicking after username"); - driver.findElement(new By.ById(SeleniumConstants.GOOGLE_NEXT_AFTER_USERNAME_BUTTON)).click(); - - LOG.info("Loggin in ... Entering password"); - By by = new By.ByName(SeleniumConstants.GOOGLE_PASSWORD_ID); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); - - LOG.info("Loggin in ... click submit"); - - waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.GOOGLE_NEXT_BUTTON_ID)).click(); - } - - public static void performFacebookLogin(WebDriver driver, LabUser user) { - LOG.info("PerformFacebookLogin"); - - driver.findElement(new By.ById(SeleniumConstants.FACEBOOK_ACCOUNT_ID)).click(); - - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(SeleniumConstants.FACEBOOK_USERNAME_ID)).sendKeys(user.getUpn()); - - LOG.info("Loggin in ... Entering password"); - By by = new By.ById(SeleniumConstants.FACEBOOK_PASSWORD_ID); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); - - waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.FACEBOOK_LOGIN_BUTTON_ID)). - click(); - } - - public static void takeScreenShot(WebDriver driver) { - String file = System.getenv("BUILD_STAGINGDIRECTORY"); - File destination = new File(file + "" + "/SeleniumError.png"); - File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); - try { - FileUtils.copyFile(scrFile, destination); - LOG.info("Screenshot can be found at: " + destination.getPath()); - } catch (Exception exception) { - LOG.error("Error taking screenshot: " + exception.getMessage()); - } - } } diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java index dd826709..a3d308d9 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java @@ -36,19 +36,16 @@ String getAadUserNameInputId() { return SeleniumConstants.WEB_UPN_INPUT_ID; } - String getADFS2019UserNameInputId() { - return SeleniumConstants.ADFS2019_UPN_INPUT_ID; + String getADFSUserNameInputId() { + return SeleniumConstants.ADFS_UPN_INPUT_ID; } private void determineFieldIds() { String federationProvider = user.getFederationProvider(); - if ("adfsv2019".equalsIgnoreCase(federationProvider)) { - passwordInputId = SeleniumConstants.ADFS2019_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.ADFS2019_SUBMIT_ID; - } else if ("adfsv4".equalsIgnoreCase(federationProvider)) { - passwordInputId = SeleniumConstants.ADFSV4_WEB_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.ADFSV4_WEB_SUBMIT_ID; + if ("adfsv2022".equalsIgnoreCase(federationProvider)) { + passwordInputId = SeleniumConstants.ADFS_PASSWORD_ID; + passwordSigInButtonId = SeleniumConstants.ADFS_SUBMIT_ID; } else { passwordInputId = SeleniumConstants.WEB_PASSWORD_ID; passwordSigInButtonId = SeleniumConstants.WEB_SUBMIT_ID; From d776f36bedf30283258cc14793b429543d79f989 Mon Sep 17 00:00:00 2001 From: avdunn Date: Thu, 20 Nov 2025 14:22:36 -0800 Subject: [PATCH 06/14] Refactor Selenium tests --- .../AcquireTokenInteractiveIT.java | 3 +- .../DeviceCodeIT.java | 8 +- .../SeleniumTest.java | 46 ++++- .../com/microsoft/aad/msal4j/labapi2/Lab.java | 4 + .../msal4j/labapi2/LabServiceParameters.java | 4 - .../microsoft/aad/msal4j/labapi2/LabUser.java | 1 + .../aad/msal4j/labapi2/LabUserHelper.java | 4 +- .../infrastructure/SeleniumExtensions.java | 173 +++++++++--------- .../infrastructure/UserInformationFields.java | 5 - 9 files changed, 136 insertions(+), 112 deletions(-) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index a49ab7cb..bf57d4da 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -44,7 +44,8 @@ void acquireTokenInteractive_ManagedUser() { LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), cfg.commonAuthority(), cfg.graphDefaultScope()); + + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getLab().getAuthority()+ "common", cfg.graphDefaultScope()); } //TODO: need to sort out ADFS 2022 configuration diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index 2ae96218..ca79c77a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -93,10 +93,10 @@ private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { seleniumDriver.findElement(new By.ById(deviceCodeFormId)).sendKeys(deviceCode.userCode()); LOG.info("Loggin in ... click continue"); - WebElement continueBtn = SeleniumExtensions.waitForElementToBeVisibleAndEnable( - seleniumDriver, - new By.ById(continueButtonId)); - continueBtn.click(); +// WebElement continueBtn = SeleniumExtensions.waitForElementToBeVisibleAndEnable( +// seleniumDriver, +// new By.ById(continueButtonId)); +// continueBtn.click(); SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); } catch (Exception e) { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java index 4f3f507b..a1a6def8 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java @@ -6,16 +6,29 @@ import com.microsoft.aad.msal4j.labapi2.LabUser; import infrastructure.SeleniumExtensions; import org.openqa.selenium.WebDriver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; abstract class SeleniumTest { + private static final Logger LOG = LoggerFactory.getLogger(SeleniumTest.class); WebDriver seleniumDriver; HttpListener httpListener; public void cleanUp() { - seleniumDriver.quit(); + if (seleniumDriver != null) { + try { + seleniumDriver.quit(); + } catch (Exception e) { + LOG.error("Error closing WebDriver: {}", e.getMessage()); + } + } if (httpListener != null) { - httpListener.stopListener(); + try { + httpListener.stopListener(); + } catch (Exception e) { + LOG.error("Error stopping HttpListener: {}", e.getMessage()); + } } } @@ -26,14 +39,27 @@ public void startUpBrowser() { void runSeleniumAutomatedLogin(LabUser user, AbstractClientApplicationBase app) { AuthorityType authorityType = app.authenticationAuthority.authorityType; - if (authorityType == AuthorityType.B2C) { - SeleniumExtensions.performLocalLogin(seleniumDriver, user); - } else if (authorityType == AuthorityType.AAD) { - SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); - } else if (authorityType == AuthorityType.ADFS) { - SeleniumExtensions.performADFSLogin(seleniumDriver, user); - } else if (authorityType == AuthorityType.CIAM || authorityType == AuthorityType.OIDC) { - SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); + try { + switch (authorityType) { + case B2C: + SeleniumExtensions.performLocalLogin(seleniumDriver, user); + break; + case AAD: + SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); + break; + case ADFS: + SeleniumExtensions.performADFSLogin(seleniumDriver, user); + break; + case CIAM: + case OIDC: + SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); + break; + default: + throw new IllegalArgumentException("Unsupported authority type: " + authorityType); + } + } catch (Exception e) { + LOG.error("Selenium automation failed for authority type {}: {}", authorityType, e.getMessage()); + throw new RuntimeException("Selenium automation failed", e); } } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java index 2f2448de..d48af54d 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java @@ -74,6 +74,10 @@ public String getTenantId() { return this.tenantId; } + public String getAuthority() { + return this.authority; + } + public String getFederationProvider() { return this.federationProvider; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java index d5e4a6d7..126c5fa8 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java @@ -7,10 +7,6 @@ public class LabServiceParameters { public enum FederationProvider { NONE, // No federation - ADFS_V4, - @Deprecated // ADFSv3 is out of support, do not use. The Arlington lab is federated to ADFSv3, so this value is needed - ADFS_V3, - ADFSv2022, CIAM, // CIAM CIAMCUD, // CIAM CUD } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java index 02fe4d0b..02ec2e0c 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java @@ -111,6 +111,7 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStringField("lastUpdatedBy", lastUpdatedBy); jsonWriter.writeStringField("lastUpdatedDate", lastUpdatedDate); jsonWriter.writeStringField("tenantId", tenantId); + jsonWriter.writeStringField("federationProvider", federationProvider); jsonWriter.writeEndObject(); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index 17fce975..8031c43f 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -68,7 +68,7 @@ public static LabResponse getLabUserData(UserQuery query) { */ private static Object getKVLabData(String secret) { try { - log.debug("Retrieving Key Vault secret: {}", secret); + log.info("Retrieving Key Vault secret: {}", secret); // Use MSAL Team vault for configuration KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsal.getSecretByName(secret); String labData = keyVaultSecret.getValue(); @@ -198,6 +198,8 @@ public static LabResponse mergeKVLabData(String... secrets) { log.info("Merged secrets [{}]: {}", String.join(", ", secrets), mergedResponse.getUser() != null ? mergedResponse.getUser().getUpn() : "N/A"); + System.out.println(mergedResponse.toJsonString()); + return mergedResponse; } catch (Exception e) { log.error("Failed to merge secrets [{}]: {}", String.join(", ", secrets), e.getMessage()); diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index c51e94d9..ea72a5d2 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -5,165 +5,164 @@ import com.microsoft.aad.msal4j.TestConstants; import com.microsoft.aad.msal4j.labapi2.LabUser; -import org.apache.commons.io.FileUtils; import org.openqa.selenium.By; -import org.openqa.selenium.OutputType; import org.openqa.selenium.StaleElementReferenceException; -import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.util.concurrent.TimeUnit; +import java.time.Duration; public class SeleniumExtensions { private static final Logger LOG = LoggerFactory.getLogger(SeleniumExtensions.class); - //These timeout values define how long Selenium will wait for elements to be visible and enabled - private static final int DEFAULT_TIMEOUT_IN_SEC = 15; - private static final int COMMON_ELEMENT_TIMEOUT_IN_SEC = 5; //Used for most elements in a sign-in flow + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); + private static final Duration COMMON_ELEMENT_TIMEOUT = Duration.ofSeconds(5); private SeleniumExtensions() { } public static WebDriver createDefaultWebDriver() { ChromeOptions options = new ChromeOptions(); - - //No visual rendering, remove to see browser window when debugging -// options.addArguments("--headless"); - //Add to avoid issues if your real browser's history/cookies are affecting tests, should not be needed in ADO pipelines + options.addArguments("--headless"); options.addArguments("--incognito"); System.setProperty("webdriver.chrome.driver", "C:/Windows/chromedriver.exe"); - ChromeDriver driver = new ChromeDriver(options); - driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); - - return driver; + return new ChromeDriver(options); } - public static WebElement waitForElementToBeVisibleAndEnable(WebDriver driver, By by, int timeOutInSeconds) { - WebDriverWait webDriverWait = new WebDriverWait(driver, timeOutInSeconds); - return webDriverWait.until(dr -> - { + public static WebElement waitForElementToBeVisibleAndEnabled(WebDriver driver, By by, Duration timeout) { + WebDriverWait wait = new WebDriverWait(driver, timeout.getSeconds()); + return wait.until(dr -> { try { - WebElement elementToBeDisplayed = driver.findElement(by); - if (elementToBeDisplayed.isDisplayed() && elementToBeDisplayed.isEnabled()) { - return elementToBeDisplayed; + WebElement element = driver.findElement(by); + if (element.isDisplayed() && element.isEnabled()) { + return element; } return null; } catch (StaleElementReferenceException e) { - LOG.info("Stale element waitForElementToBeVisibleAndEnable: " + e.getMessage()); + LOG.debug("Stale element in waitForElementToBeVisibleAndEnabled: {}", e.getMessage()); return null; } }); } - public static WebElement waitForElementToBeVisibleAndEnable(WebDriver driver, By by) { - return waitForElementToBeVisibleAndEnable(driver, by, DEFAULT_TIMEOUT_IN_SEC); + public static WebElement waitForElementToBeVisibleAndEnabled(WebDriver driver, By by) { + return waitForElementToBeVisibleAndEnabled(driver, by, DEFAULT_TIMEOUT); } public static void performADOrCiamLogin(WebDriver driver, LabUser user) { - LOG.info("performADOrCiamLogin"); + LOG.info("performADOrCiamLogin for user: {}", user.getUpn()); UserInformationFields fields = new UserInformationFields(user); - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(fields.getAadUserNameInputId())).sendKeys(user.getUpn()); + LOG.info("Entering username"); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getAadUserNameInputId())) + .sendKeys(user.getUpn()); - LOG.info("Loggin in ... Clicking after username"); - driver.findElement(new By.ById(fields.getAadSignInButtonId())).click(); + LOG.info("Clicking Next after username"); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getAadSignInButtonId())) + .click(); - LOG.info("Loggin in ... Entering password"); - By by = new By.ById(fields.getPasswordInputId()); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); + LOG.info("Entering password"); + System.out.println("Using password ID: " + fields.getPasswordInputId()); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())); + System.out.println(By.id(fields.getPasswordInputId())); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())) + .sendKeys(user.getPassword()); - LOG.info("Loggin in ... click submit"); - waitForElementToBeVisibleAndEnable(driver, new By.ById(fields.getPasswordSigInButtonId())). - click(); + LOG.info("Clicking submit"); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordSigInButtonId())) + .click(); - try { - checkAuthenticationCompletePage(driver); + if (checkAuthenticationCompletePage(driver)) { return; - } catch (TimeoutException ex) { - LOG.error("Timeout Exception while checking authentication complete page: " + ex.getMessage()); } - LOG.info("Checking optional questions"); + handleOptionalPrompts(driver); + } + private static boolean checkAuthenticationCompletePage(WebDriver driver) { try { - LOG.info("Are you trying to sign in to ... ? checking"); - waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.ARE_YOU_TRYING_TO_SIGN_IN_TO), COMMON_ELEMENT_TIMEOUT_IN_SEC). - click(); - LOG.info("Are you trying to sign in to ... ? click Continue"); - + WebDriverWait wait = new WebDriverWait(driver, COMMON_ELEMENT_TIMEOUT.getSeconds()); + wait.until(ExpectedConditions.textToBePresentInElementLocated( + By.tagName("body"), "Authentication complete")); + return true; } catch (TimeoutException ex) { - LOG.error("Timeout Exception while checking sign in prompt: " + ex.getMessage()); + LOG.debug("Authentication complete page not found: {}", ex.getMessage()); + return false; } + } + private static void handleOptionalPrompts(WebDriver driver) { + // Handle "Are you trying to sign in to..." prompt try { - LOG.info("Stay signed in? checking"); - waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.STAY_SIGN_IN_NO_BUTTON_ID), COMMON_ELEMENT_TIMEOUT_IN_SEC). - click(); - LOG.info("Stay signed in? click NO"); + LOG.info("Checking for 'Are you trying to sign in' prompt"); + waitForElementToBeVisibleAndEnabled( + driver, + By.id(SeleniumConstants.ARE_YOU_TRYING_TO_SIGN_IN_TO), + COMMON_ELEMENT_TIMEOUT) + .click(); + LOG.info("Clicked Continue on sign-in prompt"); } catch (TimeoutException ex) { - LOG.error("Timeout Exception while checking stay signed in prompt: " + ex.getMessage()); + LOG.debug("No 'Are you trying to sign in' prompt found"); } - } - private static void checkAuthenticationCompletePage(WebDriver driver) { - new WebDriverWait(driver, COMMON_ELEMENT_TIMEOUT_IN_SEC).until((ExpectedCondition) d -> { - WebElement we = d.findElement(new By.ByTagName("body")); - try { - if (we != null && we.getText().contains("Authentication complete")) - //The authentication is complete and the WebDriverWait can end - return true; - } catch (StaleElementReferenceException e) { - //It is possible for this method to begin executing before the redirect happens, in which case the WebElement - // will reference something on the previous page and cause a StaleElementReferenceException - return false; - } - return false; - }); + // Handle "Stay signed in?" prompt + try { + LOG.info("Checking for 'Stay signed in' prompt"); + waitForElementToBeVisibleAndEnabled( + driver, + By.id(SeleniumConstants.STAY_SIGN_IN_NO_BUTTON_ID), + COMMON_ELEMENT_TIMEOUT) + .click(); + LOG.info("Clicked No on 'Stay signed in' prompt"); + } catch (TimeoutException ex) { + LOG.debug("No 'Stay signed in' prompt found"); + } } public static void performADFSLogin(WebDriver driver, LabUser user) { - LOG.info("PerformADFSLogin"); + LOG.info("performADFSLogin for user: {}", user.getUpn()); UserInformationFields fields = new UserInformationFields(user); - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(fields.getADFSUserNameInputId())).sendKeys(user.getUpn()); + LOG.info("Entering username"); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getADFSUserNameInputId())) + .sendKeys(user.getUpn()); - LOG.info("Loggin in ... Entering password"); - By by = new By.ById(fields.getPasswordInputId()); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); + LOG.info("Entering password"); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())) + .sendKeys(user.getPassword()); - LOG.info("Loggin in ... click submit"); - waitForElementToBeVisibleAndEnable(driver, new By.ById(fields.getPasswordSigInButtonId())). - click(); + LOG.info("Clicking submit"); + waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordSigInButtonId())) + .click(); } public static void performLocalLogin(WebDriver driver, LabUser user) { - LOG.info("PerformLocalLogin"); + LOG.info("performLocalLogin"); - driver.findElement(new By.ById(SeleniumConstants.B2C_LOCAL_ACCOUNT_ID)).click(); + waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_ACCOUNT_ID)) + .click(); - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(SeleniumConstants.B2C_LOCAL_USERNAME_ID)).sendKeys(TestConstants.B2C_UPN); + LOG.info("Entering username"); + waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_USERNAME_ID)) + .sendKeys(TestConstants.B2C_UPN); - LOG.info("Loggin in ... Entering password"); - By by = new By.ById(SeleniumConstants.B2C_LOCAL_PASSWORD_ID); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); + LOG.info("Entering password"); + waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_PASSWORD_ID)) + .sendKeys(user.getPassword()); - waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.B2C_LOCAL_SIGN_IN_BUTTON_ID)). - click(); + LOG.info("Clicking sign in"); + waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_SIGN_IN_BUTTON_ID)) + .click(); } } diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java index a3d308d9..62b92410 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java @@ -43,12 +43,7 @@ String getADFSUserNameInputId() { private void determineFieldIds() { String federationProvider = user.getFederationProvider(); - if ("adfsv2022".equalsIgnoreCase(federationProvider)) { - passwordInputId = SeleniumConstants.ADFS_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.ADFS_SUBMIT_ID; - } else { passwordInputId = SeleniumConstants.WEB_PASSWORD_ID; passwordSigInButtonId = SeleniumConstants.WEB_SUBMIT_ID; - } } } From b67c93eb695f63025e25dde8678a5a167d821817 Mon Sep 17 00:00:00 2001 From: avdunn Date: Fri, 21 Nov 2025 10:06:11 -0800 Subject: [PATCH 07/14] Fix interactive, auth code, device code, and silent tests --- .../AcquireTokenInteractiveIT.java | 2 +- .../AcquireTokenSilentIT.java | 29 ++++++++++++------- .../AuthorizationCodeIT.java | 1 + .../DeviceCodeIT.java | 13 +++++---- .../aad/msal4j/labapi2/LabUserHelper.java | 2 -- .../infrastructure/SeleniumExtensions.java | 2 -- .../msal4j/AuthorizationResponseHandler.java | 14 +++++++-- 7 files changed, 40 insertions(+), 23 deletions(-) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index bf57d4da..d43362bd 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -45,7 +45,7 @@ void acquireTokenInteractive_ManagedUser() { LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getLab().getAuthority()+ "common", cfg.graphDefaultScope()); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", cfg.graphDefaultScope()); } //TODO: need to sort out ADFS 2022 configuration diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index ca9857a9..4671ce2f 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -6,9 +6,7 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -89,13 +87,23 @@ void acquireTokenSilent_ForceRefresh() throws Exception { } @Test - void acquireTokenSilent_usingCommonAuthority_returnCachedAt() throws Exception { - acquireTokenSilent_returnCachedTokens(cfg.organizationsAuthority()); + void acquireTokenSilent_usingOrganizationsAuthority_returnCachedAt() throws Exception { + cfg = new Config(); + + LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabUser user = labResponse.getUser(); + + acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), cfg.organizationsAuthority(), user); } @Test void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exception { - acquireTokenSilent_returnCachedTokens(cfg.tenantSpecificAuthority()); + cfg = new Config(); + + LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabUser user = labResponse.getUser(); + + acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), cfg.tenantSpecificAuthority(labResponse.getUser().getTenantId()), user); } @Test @@ -222,7 +230,7 @@ void acquireTokenSilent_TenantAsParameter() throws Exception { IAuthenticationResult resultWithTenantParam = pca.acquireTokenSilently(SilentParameters. builder(Collections.singleton(cfg.graphDefaultScope()), account). - tenant(cfg.tenant()). + tenant(labResponse.getUser().getTenantId()). build()).get(); assertResultNotNull(resultWithTenantParam); assertTokensAreNotEqual(result, resultWithTenantParam); @@ -335,12 +343,11 @@ private IConfidentialClientApplication getConfidentialClientApplications() throw build(); } - private void acquireTokenSilent_returnCachedTokens(String authority) throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + private void acquireTokenSilent_returnCachedTokens(String appId, String authority, LabUser user) throws Exception { + PublicClientApplication pca = PublicClientApplication.builder( - labResponse.getApp().getAppId()). + appId). authority(authority). build(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index ffa9047e..39d24955 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -46,6 +46,7 @@ public void acquireTokenWithAuthorizationCode_ManagedUser() { LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); + assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), null); } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index ca79c77a..d0cafb6e 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -15,6 +15,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.time.Duration; import java.util.Collections; import java.util.function.Consumer; @@ -36,7 +38,7 @@ void DeviceCodeFlowADTest() throws Exception { LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(labResponse.getApp().getAppId(), cfg.commonAuthority()); + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(labResponse.getApp().getAppId(), cfg.tenantSpecificAuthority(labResponse.getUser().getTenantId())); Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> runAutomatedDeviceCodeFlow(deviceCode, user); @@ -93,10 +95,11 @@ private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { seleniumDriver.findElement(new By.ById(deviceCodeFormId)).sendKeys(deviceCode.userCode()); LOG.info("Loggin in ... click continue"); -// WebElement continueBtn = SeleniumExtensions.waitForElementToBeVisibleAndEnable( -// seleniumDriver, -// new By.ById(continueButtonId)); -// continueBtn.click(); + WebElement continueBtn = SeleniumExtensions.waitForElementToBeVisibleAndEnabled( + seleniumDriver, + new By.ById(continueButtonId), + Duration.ofSeconds(15)); + continueBtn.click(); SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); } catch (Exception e) { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index 8031c43f..fa3f52d0 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -198,8 +198,6 @@ public static LabResponse mergeKVLabData(String... secrets) { log.info("Merged secrets [{}]: {}", String.join(", ", secrets), mergedResponse.getUser() != null ? mergedResponse.getUser().getUpn() : "N/A"); - System.out.println(mergedResponse.toJsonString()); - return mergedResponse; } catch (Exception e) { log.error("Failed to merge secrets [{}]: {}", String.join(", ", secrets), e.getMessage()); diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index ea72a5d2..593f64cb 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -72,9 +72,7 @@ public static void performADOrCiamLogin(WebDriver driver, LabUser user) { .click(); LOG.info("Entering password"); - System.out.println("Using password ID: " + fields.getPasswordInputId()); waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())); - System.out.println(By.id(fields.getPasswordInputId())); waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())) .sendKeys(user.getPassword()); diff --git a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthorizationResponseHandler.java b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthorizationResponseHandler.java index 0c3c56f1..8fa8a41b 100644 --- a/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthorizationResponseHandler.java +++ b/msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthorizationResponseHandler.java @@ -69,7 +69,7 @@ private void sendResponse(HttpExchange httpExchange, AuthorizationResult result) break; case ProtocolError: case UnknownError: - sendErrorResponse(httpExchange, getErrorResponseMessage()); + sendErrorResponse(httpExchange, result); break; } } @@ -82,7 +82,17 @@ private void sendSuccessResponse(HttpExchange httpExchange, String response) thr } } - private void sendErrorResponse(HttpExchange httpExchange, String response) throws IOException { + private void sendErrorResponse(HttpExchange httpExchange, AuthorizationResult result) throws IOException { + String response = getErrorResponseMessage(); + + // Format the message with actual error details if using default message + if (systemBrowserOptions == null || systemBrowserOptions.htmlMessageError() == null) { + String errorCode = result.error() != null ? result.error() : "unknown"; + String errorDescription = result.errorDescription() != null ? result.errorDescription() : "No description available"; + response = java.text.MessageFormat.format(response, errorCode, errorDescription); + LOG.error("Error details: error {} error_description: {}", errorCode, errorDescription); + } + if (systemBrowserOptions == null || systemBrowserOptions.browserRedirectError() == null) { send200Response(httpExchange, response); } else { From a71e033d1989cf56db1dcca97784b1b5b5845a01 Mon Sep 17 00:00:00 2001 From: avdunn Date: Fri, 21 Nov 2025 10:57:58 -0800 Subject: [PATCH 08/14] Use multi-tenant app in certain public client scenarios --- .../AcquireTokenInteractiveIT.java | 12 +++++++++++- .../AuthorizationCodeIT.java | 2 +- .../microsoft/aad/msal4j/labapi2/LabUserHelper.java | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index d43362bd..3541cfc5 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -42,7 +42,17 @@ public void startBrowser() { void acquireTokenInteractive_ManagedUser() { cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantApp(); + LabUser user = labResponse.getUser(); + + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", cfg.graphDefaultScope()); + } + + @Test + void acquireTokenInteractive_Arlington() { + cfg = new Config(); + + LabResponse labResponse = LabUserHelper.getArlingtonUser(); LabUser user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", cfg.graphDefaultScope()); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index 39d24955..2e74df7c 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -44,7 +44,7 @@ public void startBrowser() { public void acquireTokenWithAuthorizationCode_ManagedUser() { cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantApp(); LabUser user = labResponse.getUser(); assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), null); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index fa3f52d0..8f7e2d96 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -222,6 +222,10 @@ public static LabResponse getDefaultUser() { return mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); } + public static LabResponse getDefaultUserMultiTenantApp() { + return mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgsPC-JSON"); + } + public static LabResponse getDefaultAdfsUser() { return mergeKVLabData("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); } From 4583a7ddabf1cf7b0e96c76b185cc76d612ff76b Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 24 Nov 2025 10:06:41 -0800 Subject: [PATCH 09/14] Remove unused code, limit scopes, and restore final tests --- .../AuthorizationCodeIT.java | 1 - .../TokenCacheIT.java | 128 +++++++++--------- .../UsernamePasswordIT.java | 71 +++++----- .../msal4j/labapi2/AppCredentialProvider.java | 10 +- .../aad/msal4j/labapi2/AzureEnvironment.java | 5 +- .../microsoft/aad/msal4j/labapi2/Config.java | 14 +- .../labapi2/KeyVaultSecretsProvider.java | 20 +-- .../com/microsoft/aad/msal4j/labapi2/Lab.java | 8 -- .../microsoft/aad/msal4j/labapi2/LabApp.java | 1 - .../aad/msal4j/labapi2/LabConstants.java | 52 +++---- .../msal4j/labapi2/LabCredentialResponse.java | 56 -------- .../aad/msal4j/labapi2/LabHttpHelper.java | 1 + .../aad/msal4j/labapi2/LabResponse.java | 8 +- .../aad/msal4j/labapi2/LabServiceApi.java | 5 +- .../msal4j/labapi2/LabServiceParameters.java | 27 ++-- .../microsoft/aad/msal4j/labapi2/LabUser.java | 21 --- .../aad/msal4j/labapi2/LabUserHelper.java | 22 +-- .../labapi2/LabUserNotFoundException.java | 8 +- .../aad/msal4j/labapi2/UserQuery.java | 119 +++++++++------- 19 files changed, 234 insertions(+), 343 deletions(-) delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index 2e74df7c..28d6ed00 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -94,7 +94,6 @@ public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { assertEquals(resultSilent.account().username(), result.account().username()); } - //TODO: need to sort out ADFS 2022 configuration @Test @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void acquireTokenWithAuthorizationCode_ADFSv2022() { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index f8fc9536..bff8b11f 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -6,6 +6,7 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -49,72 +50,67 @@ void singleAccountInCache_RemoveAccountTest() throws Exception { assertEquals(pca.getAccounts().join().size(), 0); } - // TODO: labapi2 doesn't have guest user configuration yet - will be pulled from MSAL.NET -// @Test -// @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") -// void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exception { -// -// UserQuery query = new UserQuery(); -// query.setUserType(LabServiceParameters.UserType.GUEST); -// -// LabResponse labResponse = LabUserHelper.getLabUserData(query); -// LabUser guestUser = labResponse.getUser(); -// Lab lab = labResponse.getLab(); -// -// String dataToInitCache = TestHelper.readResource( -// this.getClass(), -// "/cache_data/remove-account-test-cache.json"); -// -// // check that cache is empty -// assertEquals(dataToInitCache, ""); -// -// ITokenCacheAccessAspect persistenceAspect = new TokenPersistence(dataToInitCache); -// -// // acquire tokens for home tenant, and serialize cache -// PublicClientApplication pca = PublicClientApplication.builder( -// guestUser.getAppId()). -// authority(TestConstants.ORGANIZATIONS_AUTHORITY) -// .setTokenCacheAccessAspect(persistenceAspect) -// .build(); -// -// pca.acquireToken(UserNamePasswordParameters. -// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), -// guestUser.getHomeUPN(), -// guestUser.getPassword().toCharArray()) -// .build()) -// .get(); -// -// String guestTenantAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + lab.getTenantId(); -// -// // initialize pca with tenant where user is guest, deserialize cache, and acquire second token -// PublicClientApplication pca2 = PublicClientApplication.builder( -// guestUser.getAppId()). -// authority(guestTenantAuthority). -// setTokenCacheAccessAspect(persistenceAspect). -// build(); -// -// pca2.acquireToken(UserNamePasswordParameters. -// builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), -// guestUser.getHomeUPN(), -// guestUser.getPassword().toCharArray()) -// .build()) -// .get(); -// -// // There should be two tokens in cache, with same accounts except for tenant -// assertEquals(pca2.getAccounts().join().iterator().next().getTenantProfiles().size(), 2); -// -// IAccount account = pca2.getAccounts().get().iterator().next(); -// -// // RemoveAccount should remove both cache entities -// pca2.removeAccount(account).join(); -// -// assertEquals(0, pca2.getAccounts().join().size()); -// -// //clean up file -// TestHelper.deleteFileContent( -// this.getClass(), -// "/cache_data/remove-account-test-cache.json"); -// } + @Test + @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") + void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exception { + + LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabUser guestUser = labResponse.getUser(); + + String dataToInitCache = TestHelper.readResource( + this.getClass(), + "/cache_data/remove-account-test-cache.json"); + + // check that cache is empty + assertEquals(dataToInitCache, ""); + + ITokenCacheAccessAspect persistenceAspect = new TokenPersistence(dataToInitCache); + + // acquire tokens for home tenant, and serialize cache + PublicClientApplication pca = PublicClientApplication.builder( + labResponse.getApp().getAppId()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY) + .setTokenCacheAccessAspect(persistenceAspect) + .build(); + + pca.acquireToken(UserNamePasswordParameters. + builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), + guestUser.getHomeUPN(), + guestUser.getPassword().toCharArray()) + .build()) + .get(); + + String guestTenantAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + guestUser.getTenantId(); + + // initialize pca with tenant where user is guest, deserialize cache, and acquire second token + PublicClientApplication pca2 = PublicClientApplication.builder( + labResponse.getApp().getAppId()). + authority(guestTenantAuthority). + setTokenCacheAccessAspect(persistenceAspect). + build(); + + pca2.acquireToken(UserNamePasswordParameters. + builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), + guestUser.getHomeUPN(), + guestUser.getPassword().toCharArray()) + .build()) + .get(); + + // There should be two tokens in cache, with same accounts except for tenant + assertEquals(pca2.getAccounts().join().iterator().next().getTenantProfiles().size(), 2); + + IAccount account = pca2.getAccounts().get().iterator().next(); + + // RemoveAccount should remove both cache entities + pca2.removeAccount(account).join(); + + assertEquals(0, pca2.getAccounts().join().size()); + + //clean up file + TestHelper.deleteFileContent( + this.getClass(), + "/cache_data/remove-account-test-cache.json"); + } private static class TokenPersistence implements ITokenCacheAccessAspect { String data; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index d88db5cc..94be0149 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -13,6 +13,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class UsernamePasswordIT { @@ -26,42 +28,39 @@ void acquireTokenWithUsernamePassword_Managed() throws Exception { assertAcquireTokenCommon(labResponse.getUser(), cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); } -// @Test -// void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { -// LabResponse labResponse = LabUserHelper.getDefaultUser(); -// LabUser user = labResponse.getUser(); -// -// assertAcquireTokenCommon( -// user, -// TestConstants.COMMON_AUTHORITY_WITH_PORT, -// TestConstants.GRAPH_DEFAULT_SCOPE, -// labResponse.getApp().getAppId()); -// } - -// @Test -// void acquireTokenWithUsernamePassword_Ciam() throws Exception { -// Map extraQueryParameters = new HashMap<>(); -// -// UserQuery query = new UserQuery(); -// query.setUserType(LabServiceParameters.UserType.CLOUD); -// query.setAzureEnvironment(LabServiceParameters.AzureEnvironment.AZURE_CIAM); -// -// LabResponse labResponse = LabUserHelper.getLabUserData(query); -// -// LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) -// .authority("https://" + user.getLabName() + ".ciamlogin.com/") -// .build(); -// -// IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. -// builder(Collections.singleton(TestConstants.USER_READ_SCOPE), -// user.getUpn(), -// user.getPassword().toCharArray()) -// .extraQueryParameters(extraQueryParameters) -// .build()) -// .get(); -// -// IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); -// } + @Test + void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { + LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabUser user = labResponse.getUser(); + + assertAcquireTokenCommon( + user, + TestConstants.MICROSOFT_AUTHORITY_HOST_WITH_PORT + user.getTenantId(), + TestConstants.GRAPH_DEFAULT_SCOPE, + labResponse.getApp().getAppId()); + } + + @Test + void acquireTokenWithUsernamePassword_Ciam() throws Exception { + Map extraQueryParameters = new HashMap<>(); + + LabResponse labResponse = LabUserHelper.getCiamCudUser(); + LabUser user = labResponse.getUser(); + + PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) + .authority("https://" + user.getLabName() + ".ciamlogin.com/") + .build(); + + IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. + builder(Collections.singleton(TestConstants.USER_READ_SCOPE), + user.getUpn(), + user.getPassword().toCharArray()) + .extraQueryParameters(extraQueryParameters) + .build()) + .get(); + + IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); + } private void assertAcquireTokenCommon(LabUser user, String authority, String scope, String appId) throws Exception { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java index 4fbd49c4..65f42971 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java @@ -5,12 +5,12 @@ public class AppCredentialProvider { - private String clientId; - private String oboClientId; - private String oboAppPassword; - private String oboAppIdURI; + private final String clientId; + private final String oboClientId; + private final String oboAppPassword; + private final String oboAppIdURI; - public AppCredentialProvider() { + AppCredentialProvider() { KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); clientId = "54a2d933-8bf8-483b-a8f8-0a31924f3c1f"; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java index 2da771a0..55d9e032 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java @@ -3,8 +3,7 @@ package com.microsoft.aad.msal4j.labapi2; -public class AzureEnvironment { +class AzureEnvironment { - public static final String AZURE = "azurecloud"; - public static final String AZURE_US_GOVERNMENT = "azureusgovernment"; + static final String AZURE_US_GOVERNMENT = "azureusgovernment"; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java index 0559da64..15a87090 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java @@ -4,11 +4,11 @@ package com.microsoft.aad.msal4j.labapi2; public class Config { - private String commonAuthority; - private String organizationsAuthority; - private String graphDefaultScope; - private AppCredentialProvider appProvider; - private String tenant; + private final String commonAuthority; + private final String organizationsAuthority; + private final String graphDefaultScope; + private final AppCredentialProvider appProvider; + private final String tenant; String azureEnvironment; @@ -34,10 +34,6 @@ public String graphDefaultScope() { return this.graphDefaultScope; } - public String tenant() { - return this.tenant; - } - public String tenantSpecificAuthority() { return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java index db6b9fcd..76f0c934 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java @@ -11,45 +11,45 @@ import com.microsoft.aad.msal4j.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; -import java.security.cert.X509Certificate; import java.security.KeyStore; import java.security.PrivateKey; +import java.security.cert.X509Certificate; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Collections; -import reactor.core.publisher.Mono; public class KeyVaultSecretsProvider implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(KeyVaultSecretsProvider.class); - public static class KeyVaultInstance { + static class KeyVaultInstance { /** * The KeyVault maintained by the MSID. It is recommended for use. */ - public static final String MSID_LAB = "https://msidlabs.vault.azure.net"; + static final String MSID_LAB = "https://msidlabs.vault.azure.net"; /** * The KeyVault maintained by the MSAL.NET team and have full control over. * Should be used temporarily - secrets should be stored and managed by MSID Lab. */ - public static final String MSAL_TEAM = "https://id4skeyvault.vault.azure.net/"; + static final String MSAL_TEAM = "https://id4skeyvault.vault.azure.net/"; } private final SecretClient secretClient; /** * Initialize the secrets provider with the specified Key Vault address. - * + *

* Authentication using client certificate: - * 1. Register Azure AD application of "Web app / API" type. - * To set up certificate based access to the application PowerShell should be used. - * 2. Add an access policy entry to target Key Vault instance for this application. + * 1. Register Azure AD application of "Web app / API" type. + * To set up certificate based access to the application PowerShell should be used. + * 2. Add an access policy entry to target Key Vault instance for this application. * * @param keyVaultAddress The Key Vault URI (defaults to MSID_LAB) */ - public KeyVaultSecretsProvider(String keyVaultAddress) { + KeyVaultSecretsProvider(String keyVaultAddress) { String vaultUrl = keyVaultAddress != null ? keyVaultAddress : KeyVaultInstance.MSID_LAB; log.debug("Initializing KeyVault secrets provider for: {}", vaultUrl); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java index d48af54d..8ba3b426 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java @@ -73,12 +73,4 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { public String getTenantId() { return this.tenantId; } - - public String getAuthority() { - return this.authority; - } - - public String getFederationProvider() { - return this.federationProvider; - } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java index a650a4e9..736a3e04 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java @@ -83,5 +83,4 @@ public String getAuthority() { public String getAppId() { return appId; } - } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java index d8281769..82fedc58 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java @@ -6,40 +6,30 @@ /** * Constants for lab API endpoints and query parameters. */ -public class LabConstants { +class LabConstants { // Base endpoints - public static final String LAB_ENDPOINT = "https://msidlab.com/api/user"; - public static final String LAB_USER_CREDENTIAL_ENDPOINT = "https://msidlab.com/api/LabSecret"; - public static final String LAB_APP_ENDPOINT = "https://msidlab.com/api/app/"; - public static final String LAB_INFO_ENDPOINT = "https://msidlab.com/api/Lab/"; + static final String LAB_ENDPOINT = "https://msidlab.com/api/user"; + static final String LAB_APP_ENDPOINT = "https://msidlab.com/api/app/"; + static final String LAB_INFO_ENDPOINT = "https://msidlab.com/api/Lab/"; // Query parameter keys - public static final String USER_TYPE = "usertype"; - public static final String MULTI_FACTOR_AUTHENTICATION = "mfa"; - public static final String PROTECTION_POLICY = "protectionpolicy"; - public static final String HOME_DOMAIN = "homedomain"; - public static final String B2C_PROVIDER = "b2cprovider"; - public static final String FEDERATION_PROVIDER = "federationprovider"; - public static final String SIGN_IN_AUDIENCE = "SignInAudience"; - public static final String AZURE_ENVIRONMENT = "azureenvironment"; - - public static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; - public static final String ARLINGTON_MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.us/"; - public static final String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; - public static final String ARLINGTON_AUTHORITY_TENANT = "arlmsidlab1.onmicrosoft.us"; - - public static final String COMMON_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "common/"; - public static final String ORGANIZATIONS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "organizations/"; - public static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; - - public static final String ARLINGTON_COMMON_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "common/"; - public static final String ARLINGTON_ORGANIZATIONS_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "organizations/"; - public static final String ARLINGTON_GRAPH_DEFAULT_SCOPE = "https://graph.microsoft.us/.default"; - public static final String GRAPH_DEFAULT_SCOPE = "https://graph.windows.net/.default"; - - public static final String ARLINGTON_APP_ID = "cb7faed4-b8c0-49ee-b421-f5ed16894c83"; - public static final String ARLINGTON_OBO_APP_ID = "c0555d2d-02f2-4838-802e-3463422e571d"; - public static final String LAB_API_SCOPE = "https://request.msidlab.com/.default"; + static final String USER_TYPE = "usertype"; + static final String MULTI_FACTOR_AUTHENTICATION = "mfa"; + static final String PROTECTION_POLICY = "protectionpolicy"; + static final String B2C_PROVIDER = "b2cprovider"; + static final String FEDERATION_PROVIDER = "federationprovider"; + static final String SIGN_IN_AUDIENCE = "SignInAudience"; + static final String AZURE_ENVIRONMENT = "azureenvironment"; + static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; + static final String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; + + static final String COMMON_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "common/"; + static final String ORGANIZATIONS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "organizations/"; + static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; + + static final String GRAPH_DEFAULT_SCOPE = "https://graph.windows.net/.default"; + + static final String LAB_API_SCOPE = "https://request.msidlab.com/.default"; } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java deleted file mode 100644 index dc80ac15..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabCredentialResponse.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi2; - -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -/** - * Response object for user credential requests from lab API. - */ -public class LabCredentialResponse implements JsonSerializable { - - private String secret; - - public String getSecret() { - return secret; - } - - public void setSecret(String secret) { - this.secret = secret; - } - - /** - * Deserialize a LabCredentialResponse from JSON. - */ - public static LabCredentialResponse fromJson(JsonReader jsonReader) throws IOException { - return jsonReader.readObject(reader -> { - LabCredentialResponse response = new LabCredentialResponse(); - - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - - if ("secret".equals(fieldName)) { - response.secret = reader.getString(); - } else { - reader.skipChildren(); - } - } - return response; - }); - } - - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - jsonWriter.writeStringField("secret", secret); - jsonWriter.writeEndObject(); - return jsonWriter; - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java index b4cdc569..eb2014f2 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java @@ -28,6 +28,7 @@ static String sendRequestToLab(String url, String accessToken) throws IOExceptio static String sendRequestToLab(URL labUrl, String accessToken) throws IOException { log.debug("Sending HTTP GET request to: {}", labUrl); + System.out.println(labUrl); HttpsURLConnection conn = (HttpsURLConnection) labUrl.openConnection(); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java index dd6392e7..2f0c2c66 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java @@ -23,7 +23,7 @@ public LabUser getUser() { return user; } - public void setUser(LabUser user) { + void setUser(LabUser user) { this.user = user; } @@ -31,7 +31,7 @@ public LabApp getApp() { return app; } - public void setApp(LabApp app) { + void setApp(LabApp app) { this.app = app; } @@ -39,14 +39,14 @@ public Lab getLab() { return lab; } - public void setLab(Lab lab) { + void setLab(Lab lab) { this.lab = lab; } /** * Deserialize a LabResponse from JSON. */ - public static LabResponse fromJson(JsonReader jsonReader) throws IOException { + static LabResponse fromJson(JsonReader jsonReader) throws IOException { return jsonReader.readObject(reader -> { LabResponse response = new LabResponse(); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java index 37d0c4b9..9ba207f9 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java @@ -35,7 +35,7 @@ public class LabServiceApi { * @param query Any and all parameters that the returned user should satisfy * @return LabResponse with user that matches the query */ - public LabResponse getLabResponseFromApi(UserQuery query) { + LabResponse getLabResponseFromApi(UserQuery query) { log.debug("Querying lab API for user with parameters: {}", query); try { @@ -170,7 +170,7 @@ private String runQuery(UserQuery query) { if (query.getAzureEnvironment() != null) { queryDict.put(LabConstants.AZURE_ENVIRONMENT, - query.getAzureEnvironment().toString()); + query.getAzureEnvironment()); } return sendLabRequest(LabConstants.LAB_ENDPOINT, queryDict); @@ -188,7 +188,6 @@ private String sendLabRequest(String requestUrl, Map queryDict) log.debug("Acquiring lab access token"); String accessToken = getLabAccessToken(); log.debug("Sending lab request to: {} with {} query parameters", requestUrl, queryDict.size()); - String response = LabHttpHelper.sendRequestToLab(requestUrl, queryDict, accessToken); log.debug("Lab request successful, received {} characters", response.length()); return response; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java index 126c5fa8..01b3bade 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java @@ -3,37 +3,32 @@ package com.microsoft.aad.msal4j.labapi2; -public class LabServiceParameters { +class LabServiceParameters { - public enum FederationProvider { - NONE, // No federation - CIAM, // CIAM - CIAMCUD, // CIAM CUD + enum FederationProvider { + CIAMCUD // CIAM CUD } - public enum B2CIdentityProvider { - LOCAL, // Local B2C account - MSA + enum B2CIdentityProvider { + LOCAL // Local B2C account } - public enum UserType { + enum UserType { B2C, CLOUD, FEDERATED, - ONPREM, MSA } - public enum MFA { - NONE, + enum MFA { + NONE } - public enum ProtectionPolicy { - NONE, + enum ProtectionPolicy { + NONE } - public enum SignInAudience - { + enum SignInAudience { AzureAdMyOrg } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java index 02ec2e0c..0fc95d9e 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java @@ -130,18 +130,6 @@ public String getLabName() { return this.labName; } - public String getB2cProvider() { - return this.b2cProvider; - } - - public String getUserType() { - return this.userType; - } - - public String getHomeDomain() { - return this.homeDomain; - } - public String getHomeUPN() { return this.homeUPN; } @@ -171,13 +159,4 @@ public String getPassword() { public String getFederationProvider() { return federationProvider != null ? federationProvider : "none"; } - - /** - * Set the federation provider for this user. - * - * @param federationProvider The federation provider string - */ - public void setFederationProvider(String federationProvider) { - this.federationProvider = federationProvider; - } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index 8f7e2d96..edd9b9db 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -37,7 +37,7 @@ public class LabUserHelper { * @param query The UserQuery to search for * @return LabResponse */ - public static LabResponse getLabUserData(UserQuery query) { + static LabResponse getLabUserData(UserQuery query) { if (userCache.containsKey(query)) { LabResponse cached = userCache.get(query); log.debug("Lab cache hit: {}", @@ -114,7 +114,7 @@ private static Object getKVLabData(String secret) { * @param userLabName The lab name of the user (used as secret name) * @return The user's password */ - public static String fetchUserPassword(String userLabName) { + static String fetchUserPassword(String userLabName) { if (userLabName == null || userLabName.trim().isEmpty()) { log.error("Password fetch failed: empty lab name"); throw new IllegalArgumentException( @@ -156,7 +156,7 @@ public static String fetchUserPassword(String userLabName) { * @param secrets Array of Key Vault secret names to merge * @return Merged LabResponse */ - public static LabResponse mergeKVLabData(String... secrets) { + static LabResponse mergeKVLabData(String... secrets) { if (secrets == null || secrets.length == 0) { throw new IllegalArgumentException( "At least one secret name must be provided."); @@ -239,16 +239,12 @@ public static LabResponse getArlingtonUser() { return getLabUserData(UserQuery.arlingtonUserQuery()); } - public static LabResponse getArlingtonADFSUser() { + static LabResponse getArlingtonADFSUser() { // Create a modified query with federated user type UserQuery query = UserQuery.arlingtonUserQuery(); query.setUserType(LabServiceParameters.UserType.FEDERATED); - return getLabUserData(query); - } - - public static LabResponse getCiamUser() { - return getLabUserData(UserQuery.ciamUserQuery()); + return getLabUserData(query); } public static LabResponse getCiamCudUser() { @@ -258,12 +254,4 @@ public static LabResponse getCiamCudUser() { public static LabResponse getMSAUser() { return getLabUserData(UserQuery.msaUserQuery()); } - - public static LabResponse getB2CMsaAccount() { - return getLabUserData(UserQuery.b2cMsaAccountQuery()); - } - - public static LabResponse getCiamOboUser() { - return getLabUserData(UserQuery.ciamOboUserQuery()); - } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java index 127f42db..33d549a6 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java @@ -6,19 +6,15 @@ /** * Exception thrown when a lab user matching the query cannot be found. */ -public class LabUserNotFoundException extends RuntimeException { +class LabUserNotFoundException extends RuntimeException { private final UserQuery query; - public LabUserNotFoundException(UserQuery query, String message) { + LabUserNotFoundException(UserQuery query, String message) { super(message); this.query = query; } - public UserQuery getQuery() { - return query; - } - @Override public String toString() { return String.format("LabUserNotFoundException{query=%s, message=%s}", diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java index f49ecfc3..482ab118 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java @@ -3,40 +3,59 @@ package com.microsoft.aad.msal4j.labapi2; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.*; +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.B2CIdentityProvider; +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.FederationProvider; +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.SignInAudience; +import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.UserType; -public class UserQuery { +class UserQuery { private UserType userType; private B2CIdentityProvider b2cIdentityProvider; private FederationProvider federationProvider; private SignInAudience signInAudience; private String azureEnvironment; - // Getters and Setters - public UserType getUserType() { return userType; } - public void setUserType(UserType userType) { this.userType = userType; } + UserType getUserType() { + return userType; + } + + void setUserType(UserType userType) { + this.userType = userType; + } - public B2CIdentityProvider getB2cIdentityProvider() { return b2cIdentityProvider; } - public void setB2cIdentityProvider(B2CIdentityProvider b2cIdentityProvider) { + B2CIdentityProvider getB2cIdentityProvider() { + return b2cIdentityProvider; + } + + void setB2cIdentityProvider(B2CIdentityProvider b2cIdentityProvider) { this.b2cIdentityProvider = b2cIdentityProvider; } - public FederationProvider getFederationProvider() { return federationProvider; } - public void setFederationProvider(FederationProvider federationProvider) { + FederationProvider getFederationProvider() { + return federationProvider; + } + + void setFederationProvider(FederationProvider federationProvider) { this.federationProvider = federationProvider; } - public String getAzureEnvironment() { return azureEnvironment; } - public void setAzureEnvironment(String azureEnvironment) { + String getAzureEnvironment() { + return azureEnvironment; + } + + void setAzureEnvironment(String azureEnvironment) { this.azureEnvironment = azureEnvironment; } - public SignInAudience getSignInAudience() { return signInAudience; } - public void setSignInAudience(SignInAudience signInAudience) { + SignInAudience getSignInAudience() { + return signInAudience; + } + + void setSignInAudience(SignInAudience signInAudience) { this.signInAudience = signInAudience; } - public static UserQuery arlingtonUserQuery() { + static UserQuery arlingtonUserQuery() { UserQuery query = new UserQuery(); query.setUserType(UserType.CLOUD); query.setAzureEnvironment(AzureEnvironment.AZURE_US_GOVERNMENT); @@ -46,39 +65,18 @@ public static UserQuery arlingtonUserQuery() { /** * Gets a B2C local account (username/password) from the lab. */ - public static UserQuery b2cLocalAccountQuery() { + static UserQuery b2cLocalAccountQuery() { UserQuery query = new UserQuery(); query.setUserType(UserType.B2C); query.setB2cIdentityProvider(B2CIdentityProvider.LOCAL); return query; } - /** - * Gets a B2C user that authenticates with Microsoft Account (MSA). - */ - public static UserQuery b2cMsaAccountQuery() { - UserQuery query = new UserQuery(); - query.setUserType(UserType.B2C); - query.setB2cIdentityProvider(B2CIdentityProvider.MSA); - return query; - } - - /** - * Gets a CIAM user with standard domain (tenant.ciamlogin.com). - * Uses the Lab API to query for CIAM users. - */ - public static UserQuery ciamUserQuery() { - UserQuery query = new UserQuery(); - query.setFederationProvider(FederationProvider.CIAM); - query.setSignInAudience(SignInAudience.AzureAdMyOrg); - return query; - } - /** * Gets a CIAM user with Custom User Domain (CUD). * Example: login.customdomain.com instead of tenant.ciamlogin.com */ - public static UserQuery ciamCudUserQuery() { + static UserQuery ciamCudUserQuery() { UserQuery query = new UserQuery(); query.setFederationProvider(FederationProvider.CIAMCUD); query.setSignInAudience(SignInAudience.AzureAdMyOrg); @@ -89,24 +87,12 @@ public static UserQuery ciamCudUserQuery() { * Gets a regular Microsoft Account (MSA) user, not tied to B2C. * This is for consumer accounts like outlook.com, hotmail.com, etc. */ - public static UserQuery msaUserQuery() { + static UserQuery msaUserQuery() { UserQuery query = new UserQuery(); query.setUserType(UserType.MSA); return query; } - /** - * Gets a CIAM user for OBO scenarios. - * CIAM supports OBO flows, so this query gets a CIAM user that can be used - * in OBO tests. - */ - public static UserQuery ciamOboUserQuery() { - UserQuery query = new UserQuery(); - query.setFederationProvider(FederationProvider.CIAM); - query.setSignInAudience(SignInAudience.AzureAdMyOrg); - return query; - } - @Override public int hashCode() { // Implement proper hashCode for caching @@ -127,4 +113,37 @@ public boolean equals(Object obj) { java.util.Objects.equals(azureEnvironment, other.azureEnvironment) && java.util.Objects.equals(b2cIdentityProvider, other.b2cIdentityProvider); } + + //Formats the fields as they would be seen in the query parameters section of a URL + @Override + public String toString() { + StringBuilder queryString = new StringBuilder(); + + if (userType != null) { + queryString.append("userType=").append(userType).append("&"); + } + + if (b2cIdentityProvider != null) { + queryString.append("b2cIdentityProvider=").append(b2cIdentityProvider).append("&"); + } + + if (federationProvider != null) { + queryString.append("federationProvider=").append(federationProvider).append("&"); + } + + if (signInAudience != null) { + queryString.append("signInAudience=").append(signInAudience).append("&"); + } + + if (azureEnvironment != null) { + queryString.append("azureEnvironment=").append(azureEnvironment).append("&"); + } + + // Remove trailing "&" if any parameters were added + if (queryString.length() > 0) { + queryString.setLength(queryString.length() - 1); + } + + return "?" + queryString; + } } \ No newline at end of file From e2748372339f7bc16c857353fdd9d25690de0577 Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 24 Nov 2025 14:22:45 -0800 Subject: [PATCH 10/14] Refactor and clean up new test infrastructure and existing Selenium helpers --- .../AcquireTokenInteractiveIT.java | 23 +- .../AcquireTokenSilentIT.java | 86 +++---- .../AuthorizationCodeIT.java | 18 +- .../ClientCredentialsIT.java | 25 +- .../DeviceCodeIT.java | 36 +-- .../OnBehalfOfIT.java | 33 +-- .../RefreshTokenIT.java | 12 +- .../TestConstants.java | 15 +- .../UsernamePasswordIT.java | 10 +- .../msal4j/labapi2/AppCredentialProvider.java | 37 --- .../microsoft/aad/msal4j/labapi2/Config.java | 48 ---- .../aad/msal4j/labapi2/KeyVaultRegistry.java | 22 ++ .../labapi2/KeyVaultSecretsProvider.java | 153 ++++++++++++ .../aad/msal4j/labapi2/LabConstants.java | 6 - .../aad/msal4j/labapi2/LabHttpHelper.java | 2 - .../aad/msal4j/labapi2/LabServiceApi.java | 33 ++- .../microsoft/aad/msal4j/labapi2/LabUser.java | 3 +- .../aad/msal4j/labapi2/LabUserHelper.java | 229 +----------------- .../infrastructure/SeleniumConstants.java | 33 --- .../infrastructure/SeleniumExtensions.java | 156 ++++-------- .../infrastructure/UserInformationFields.java | 49 ---- .../pageobjects/ADFSLoginPage.java | 90 +++++++ .../pageobjects/AzureADLoginPage.java | 176 ++++++++++++++ .../pageobjects/B2CLocalLoginPage.java | 104 ++++++++ 24 files changed, 707 insertions(+), 692 deletions(-) delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java delete mode 100644 msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java delete mode 100644 msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java create mode 100644 msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/ADFSLoginPage.java create mode 100644 msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/AzureADLoginPage.java create mode 100644 msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/B2CLocalLoginPage.java diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index 3541cfc5..d549074a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -26,8 +26,6 @@ class AcquireTokenInteractiveIT extends SeleniumTest { private static final Logger LOG = LoggerFactory.getLogger(AcquireTokenInteractiveIT.class); - private Config cfg; - @AfterEach public void stopBrowser() { cleanUp(); @@ -40,25 +38,20 @@ public void startBrowser() { @Test void acquireTokenInteractive_ManagedUser() { - cfg = new Config(); - - LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantApp(); + LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantAppPublicClient(); LabUser user = labResponse.getUser(); - assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", cfg.graphDefaultScope()); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", TestConstants.GRAPH_DEFAULT_SCOPE); } @Test void acquireTokenInteractive_Arlington() { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getArlingtonUser(); LabUser user = labResponse.getUser(); - assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", cfg.graphDefaultScope()); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", TestConstants.GRAPH_DEFAULT_SCOPE); } - //TODO: need to sort out ADFS 2022 configuration @Test() @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void acquireTokenInteractive_ADFSv2022() { @@ -70,8 +63,6 @@ void acquireTokenInteractive_ADFSv2022() { @Test void acquireTokenWithAuthorizationCode_B2C_Local() { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY); @@ -79,8 +70,6 @@ void acquireTokenWithAuthorizationCode_B2C_Local() { @Test void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT); @@ -88,8 +77,6 @@ void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { @Test void acquireTokenInteractive_ManagedUser_InstanceAware() { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getArlingtonUser(); LabUser user = labResponse.getUser(); assertAcquireTokenInstanceAware(user, labResponse.getApp().getAppId(), labResponse.getLab().getTenantId()); @@ -172,7 +159,7 @@ private void assertAcquireTokenB2C(LabUser user, String authority) { private void assertAcquireTokenInstanceAware(LabUser user, String appId, String tenantId) { PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, TestConstants.MICROSOFT_AUTHORITY_HOST + tenantId); - IAuthenticationResult result = acquireTokenInteractive_instanceAware(user, pca, cfg.graphDefaultScope()); + IAuthenticationResult result = acquireTokenInteractive_instanceAware(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); assertEquals(user.getUpn(), result.account().username()); @@ -185,7 +172,7 @@ private void assertAcquireTokenInstanceAware(LabUser user, String appId, String IAuthenticationResult cachedResult; try { - cachedResult = acquireTokenSilently(pca, result.account(), cfg.graphDefaultScope()); + cachedResult = acquireTokenSilently(pca, result.account(), TestConstants.GRAPH_DEFAULT_SCOPE); } catch (Exception ex) { throw new RuntimeException(ex.getMessage()); } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index 4671ce2f..2ef840f8 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -20,38 +20,32 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AcquireTokenSilentIT { - private Config cfg; - @Test void acquireTokenSilent_OrganizationAuthority_TokenRefreshed() throws Exception { - cfg = new Config(); - // When using common, organization, or consumer tenants, cache has no way // of determining which access token to return therefore token is always refreshed IPublicClientApplication pca = getPublicClientApplicationWithTokensInCache(); IAccount account = pca.getAccounts().join().iterator().next(); - IAuthenticationResult result = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), false); + IAuthenticationResult result = acquireTokenSilently(pca, account, TestConstants.GRAPH_DEFAULT_SCOPE, false); assertResultNotNull(result); } @Test void acquireTokenSilent_LabAuthority_TokenNotRefreshed() throws Exception { - cfg = new Config(); - // Access token should be returned from cache, and not using refresh token LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); - IAuthenticationResult result = acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); + IAuthenticationResult result = acquireTokenUsernamePassword(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); IAccount account = pca.getAccounts().join().iterator().next(); - IAuthenticationResult acquireSilentResult = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), false); + IAuthenticationResult acquireSilentResult = acquireTokenSilently(pca, account, TestConstants.GRAPH_DEFAULT_SCOPE, false); assertResultNotNull(result); // Check that access and id tokens are coming from cache @@ -63,21 +57,19 @@ void acquireTokenSilent_LabAuthority_TokenNotRefreshed() throws Exception { @Test void acquireTokenSilent_ForceRefresh() throws Exception { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); - IAuthenticationResult result = acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); + IAuthenticationResult result = acquireTokenUsernamePassword(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); assertResultNotNull(result); IAccount account = pca.getAccounts().join().iterator().next(); - IAuthenticationResult resultAfterRefresh = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), true); + IAuthenticationResult resultAfterRefresh = acquireTokenSilently(pca, account, TestConstants.GRAPH_DEFAULT_SCOPE, true); assertResultNotNull(resultAfterRefresh); // Check that new refresh and id tokens are being returned @@ -88,34 +80,28 @@ void acquireTokenSilent_ForceRefresh() throws Exception { @Test void acquireTokenSilent_usingOrganizationsAuthority_returnCachedAt() throws Exception { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), cfg.organizationsAuthority(), user); + acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), TestConstants.ORGANIZATIONS_AUTHORITY, user); } @Test void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exception { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), cfg.tenantSpecificAuthority(labResponse.getUser().getTenantId()), user); + acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId(), user); } @Test void acquireTokenSilent_ConfidentialClient_acquireTokenSilent() throws Exception { - cfg = new Config(); - IConfidentialClientApplication cca = getConfidentialClientApplications(); //test that adding extra query parameters does not break the flow Map extraParameters = new HashMap<>(); extraParameters.put("test","test"); IAuthenticationResult result = cca.acquireToken(ClientCredentialParameters - .builder(Collections.singleton(cfg.graphDefaultScope())) + .builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE)) .extraQueryParameters(extraParameters) .build()) .get(); @@ -126,7 +112,7 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilent() throws Exception String cachedAt = result.accessToken(); result = cca.acquireTokenSilently(SilentParameters - .builder(Collections.singleton(cfg.graphDefaultScope())) + .builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE)) .extraQueryParameters(extraParameters) .build()) .get(); @@ -138,7 +124,6 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilent() throws Exception @Test void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrowsException() throws Exception { - cfg = new Config(); IConfidentialClientApplication cca = getConfidentialClientApplications(); @@ -152,27 +137,25 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrow //Acquiring token for different scope, expect exception to be thrown assertThrows(ExecutionException.class, () -> cca.acquireTokenSilently(SilentParameters - .builder(Collections.singleton(cfg.graphDefaultScope())) + .builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE)) .build()) .get()); } @Test void acquireTokenSilent_WithRefreshOn() throws Exception { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); - IAuthenticationResult resultOriginal = acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); + IAuthenticationResult resultOriginal = acquireTokenUsernamePassword(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); assertResultNotNull(resultOriginal); - IAuthenticationResult resultSilent = acquireTokenSilently(pca, resultOriginal.account(), cfg.graphDefaultScope(), false); + IAuthenticationResult resultSilent = acquireTokenSilently(pca, resultOriginal.account(), TestConstants.GRAPH_DEFAULT_SCOPE, false); assertNotNull(resultSilent); assertTokensAreEqual(resultOriginal, resultSilent); @@ -186,7 +169,7 @@ void acquireTokenSilent_WithRefreshOn() throws Exception { token.refreshOn(Long.toString(currTimestampSec + 60)); pca.tokenCache.accessTokens.put(key, token); - IAuthenticationResult resultSilentWithRefreshOn = acquireTokenSilently(pca, resultOriginal.account(), cfg.graphDefaultScope(), false); + IAuthenticationResult resultSilentWithRefreshOn = acquireTokenSilently(pca, resultOriginal.account(), TestConstants.GRAPH_DEFAULT_SCOPE, false); //Current time is before refreshOn, so token should not have been refreshed assertNotNull(resultSilentWithRefreshOn); assertEquals(pca.tokenCache.accessTokens.get(key).refreshOn(), Long.toString(currTimestampSec + 60)); @@ -196,7 +179,7 @@ void acquireTokenSilent_WithRefreshOn() throws Exception { token.refreshOn(Long.toString(currTimestampSec - 60)); pca.tokenCache.accessTokens.put(key, token); - resultSilentWithRefreshOn = acquireTokenSilently(pca, resultOriginal.account(), cfg.graphDefaultScope(), false); + resultSilentWithRefreshOn = acquireTokenSilently(pca, resultOriginal.account(), TestConstants.GRAPH_DEFAULT_SCOPE, false); //Current time is after refreshOn, so token should be refreshed assertNotNull(resultSilentWithRefreshOn); assertTokensAreNotEqual(resultSilent, resultSilentWithRefreshOn); @@ -206,30 +189,28 @@ void acquireTokenSilent_WithRefreshOn() throws Exception { @Test void acquireTokenSilent_TenantAsParameter() throws Exception { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. - builder(Collections.singleton(cfg.graphDefaultScope()), + builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), user.getUpn(), user.getPassword().toCharArray()) .build()).get(); assertResultNotNull(result); IAccount account = pca.getAccounts().join().iterator().next(); - IAuthenticationResult silentResult = acquireTokenSilently(pca, account, cfg.graphDefaultScope(), false); + IAuthenticationResult silentResult = acquireTokenSilently(pca, account, TestConstants.GRAPH_DEFAULT_SCOPE, false); assertResultNotNull(silentResult); assertTokensAreEqual(result, silentResult); IAuthenticationResult resultWithTenantParam = pca.acquireTokenSilently(SilentParameters. - builder(Collections.singleton(cfg.graphDefaultScope()), account). + builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), account). tenant(labResponse.getUser().getTenantId()). build()).get(); assertResultNotNull(resultWithTenantParam); @@ -238,13 +219,12 @@ void acquireTokenSilent_TenantAsParameter() throws Exception { @Test void acquireTokenSilent_emptyStringScope() throws Exception { - cfg = new Config(); LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); String emptyScope = StringHelper.EMPTY_STRING; @@ -259,14 +239,13 @@ void acquireTokenSilent_emptyStringScope() throws Exception { @Test void acquireTokenSilent_emptyScopeSet() throws Exception { - cfg = new Config(); LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. @@ -289,14 +268,13 @@ void acquireTokenSilent_emptyScopeSet() throws Exception { @Test public void acquireTokenSilent_ClaimsForceRefresh() throws Exception { - cfg = new Config(); LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. @@ -331,33 +309,31 @@ public void acquireTokenSilent_ClaimsForceRefresh() throws Exception { } private IConfidentialClientApplication getConfidentialClientApplications() throws Exception { - String clientId = cfg.appProvider().getOboAppId(); - String password = cfg.appProvider().getOboAppPassword(); + String clientId = TestConstants.OBO_CLIENT_ID; + String password = KeyVaultRegistry.getMsalTeamProvider().getSecretByName("IdentityDivisionDotNetOBOServiceSecret").getValue(); IClientCredential credential = ClientCredentialFactory.createFromSecret(password); return ConfidentialClientApplication.builder( clientId, credential). - //authority(MICROSOFT_AUTHORITY) - authority(cfg.tenantSpecificAuthority()). + authority(TestConstants.AUTHORITY_PUBLIC_TENANT_SPECIFIC). build(); } private void acquireTokenSilent_returnCachedTokens(String appId, String authority, LabUser user) throws Exception { - PublicClientApplication pca = PublicClientApplication.builder( appId). authority(authority). build(); - IAuthenticationResult interactiveAuthResult = acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); + IAuthenticationResult interactiveAuthResult = acquireTokenUsernamePassword(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); assertNotNull(interactiveAuthResult); IAuthenticationResult silentAuthResult = pca.acquireTokenSilently( SilentParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), interactiveAuthResult.account()) + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), interactiveAuthResult.account()) .build()) .get(); @@ -372,10 +348,10 @@ private IPublicClientApplication getPublicClientApplicationWithTokensInCache() PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); - acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); + acquireTokenUsernamePassword(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); return pca; } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index 28d6ed00..2ef9b3eb 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -27,9 +27,6 @@ class AuthorizationCodeIT extends SeleniumTest { private static final Logger LOG = LoggerFactory.getLogger(AuthorizationCodeIT.class); - private Config cfg; - - @AfterEach public void stopBrowser() { cleanUp(); @@ -42,9 +39,7 @@ public void startBrowser() { @Test public void acquireTokenWithAuthorizationCode_ManagedUser() { - cfg = new Config(); - - LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantApp(); + LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantAppPublicClient(); LabUser user = labResponse.getUser(); assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), null); @@ -52,8 +47,6 @@ public void acquireTokenWithAuthorizationCode_ManagedUser() { @Test public void acquireTokenWithAuthorizationCode_B2C_Local() { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); LabUser user = labResponse.getUser(); assertAcquireTokenB2C(user); @@ -126,13 +119,13 @@ private void assertAcquireTokenADFS(LabUser user, String appId, String authority private void assertAcquireTokenAAD(LabUser user, String appId, Map> parameters) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, cfg.commonAuthority()); + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, TestConstants.COMMON_AUTHORITY); String authCode = acquireAuthorizationCodeAutomated(user, pca, parameters); IAuthenticationResult result = acquireTokenAuthorizationCodeFlow( pca, authCode, - Collections.singleton(cfg.graphDefaultScope())); + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE)); IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); assertEquals(user.getUpn(), result.account().username()); @@ -140,9 +133,8 @@ private void assertAcquireTokenAAD(LabUser user, String appId, Map deviceCodeConsumer = (DeviceCode deviceCode) -> runAutomatedDeviceCodeFlow(deviceCode, user); IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters - .builder(Collections.singleton(cfg.graphDefaultScope()), + .builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), deviceCodeConsumer) .build()) .get(); @@ -84,28 +79,11 @@ void DeviceCodeFlowMSATest() throws Exception { } private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { - - try { - String deviceCodeFormId; - String continueButtonId; - deviceCodeFormId = "otc"; - continueButtonId = "idSIButton9"; - LOG.info("Loggin in ... Entering device code"); - seleniumDriver.navigate().to(deviceCode.verificationUri()); - seleniumDriver.findElement(new By.ById(deviceCodeFormId)).sendKeys(deviceCode.userCode()); - - LOG.info("Loggin in ... click continue"); - WebElement continueBtn = SeleniumExtensions.waitForElementToBeVisibleAndEnabled( - seleniumDriver, - new By.ById(continueButtonId), - Duration.ofSeconds(15)); - continueBtn.click(); - - SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); - } catch (Exception e) { - LOG.error("Browser automation failed: {}", e.getMessage()); - throw new RuntimeException("Browser automation failed: " + e.getMessage()); - } + SeleniumExtensions.performDeviceCodeLogin( + seleniumDriver, + deviceCode.verificationUri(), + deviceCode.userCode(), + user); } @AfterAll diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java index e76363f2..fb45738d 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java @@ -6,8 +6,6 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -17,24 +15,21 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class OnBehalfOfIT { - private Config cfg; - @Test void acquireTokenWithOBO_Managed() throws Exception { - cfg = new Config(); String accessToken = this.getAccessToken(); - final String clientId = cfg.appProvider().getOboAppId(); - final String password = cfg.appProvider().getOboAppPassword(); + String clientId = TestConstants.OBO_CLIENT_ID; + String password = KeyVaultRegistry.getMsalTeamProvider().getSecretByName("IdentityDivisionDotNetOBOServiceSecret").getValue(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). - authority(cfg.tenantSpecificAuthority("10c419d4-4a50-45b2-aa4e-919fb84df24f")). + authority(TestConstants.MICROSOFT_AUTHORITY_HOST + "10c419d4-4a50-45b2-aa4e-919fb84df24f"). build(); IAuthenticationResult result = cca.acquireToken(OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(accessToken)).build()). get(); @@ -43,16 +38,14 @@ void acquireTokenWithOBO_Managed() throws Exception { @Test void acquireTokenWithOBO_testCache() throws Exception { - cfg = new Config(); - String accessToken = this.getAccessToken(); - final String clientId = cfg.appProvider().getOboAppId(); - final String password = cfg.appProvider().getOboAppPassword(); + String clientId = TestConstants.OBO_CLIENT_ID; + String password = KeyVaultRegistry.getMsalTeamProvider().getSecretByName("IdentityDivisionDotNetOBOServiceSecret").getValue(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). - authority(cfg.tenantSpecificAuthority("10c419d4-4a50-45b2-aa4e-919fb84df24f")). + authority(TestConstants.MICROSOFT_AUTHORITY_HOST + "10c419d4-4a50-45b2-aa4e-919fb84df24f"). build(); IAuthenticationResult result1 = @@ -75,7 +68,7 @@ void acquireTokenWithOBO_testCache() throws Exception { // Scope 2, should return new token IAuthenticationResult result3 = cca.acquireToken(OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(accessToken)).build()). get(); @@ -85,7 +78,7 @@ void acquireTokenWithOBO_testCache() throws Exception { // Scope 2, should return cached token IAuthenticationResult result4 = cca.acquireToken(OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(accessToken)).build()). get(); @@ -95,7 +88,7 @@ void acquireTokenWithOBO_testCache() throws Exception { IAuthenticationResult result5 = cca.acquireToken( OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(accessToken)) .skipCache(true) .build()). @@ -111,7 +104,7 @@ void acquireTokenWithOBO_testCache() throws Exception { IAuthenticationResult result6 = cca.acquireToken( OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(newAccessToken)) .build()). get(); @@ -132,8 +125,8 @@ private String getAccessToken() throws Exception { LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); - String clientId = cfg.appProvider().getAppId(); - String apiReadScope = cfg.appProvider().getOboAppIdURI() + "/access_as_user"; + String clientId = TestConstants.OBO_CLIENT_ID; + String apiReadScope = TestConstants.OBO_APP_ID_URI + "/access_as_user"; PublicClientApplication pca = PublicClientApplication.builder( clientId). authority("https://login.microsoftonline.com/organizations"). diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java index abb8df0f..3e7fce67 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java @@ -6,8 +6,6 @@ import com.microsoft.aad.msal4j.labapi2.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -18,19 +16,17 @@ class RefreshTokenIT { private String refreshToken; private PublicClientApplication pca; - private Config cfg; - private void setUp() throws Exception { LabResponse labResponse = LabUserHelper.getDefaultUser(); LabUser user = labResponse.getUser(); pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). - authority(cfg.organizationsAuthority()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); AuthenticationResult result = (AuthenticationResult) pca.acquireToken(UserNamePasswordParameters - .builder(Collections.singleton(cfg.graphDefaultScope()), + .builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), user.getUpn(), user.getPassword().toCharArray()) .build()) @@ -41,13 +37,11 @@ private void setUp() throws Exception { @Test void acquireTokenWithRefreshToken() throws Exception { - cfg = new Config(); - setUp(); IAuthenticationResult result = pca.acquireToken(RefreshTokenParameters .builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), refreshToken) .build()) .get(); diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java index 0db1ba37..e0811e87 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java @@ -18,23 +18,14 @@ public class TestConstants { public static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; public static final String MICROSOFT_AUTHORITY_BASIC_HOST = "login.microsoftonline.com"; public static final String MICROSOFT_AUTHORITY_HOST_WITH_PORT = "https://login.microsoftonline.com:443/"; - public static final String ARLINGTON_MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.us/"; public static final String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; - public static final String ARLINGTON_AUTHORITY_TENANT = "arlmsidlab1.onmicrosoft.us"; public static final String ORGANIZATIONS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "organizations/"; public static final String COMMON_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "common/"; public static final String CONSUMERS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "consumers/"; - public static final String COMMON_AUTHORITY_WITH_PORT = MICROSOFT_AUTHORITY_HOST_WITH_PORT + "msidlab4.onmicrosoft.com"; public static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; - public static final String TENANT_SPECIFIC_AUTHORITY = MICROSOFT_AUTHORITY_HOST + MICROSOFT_AUTHORITY_TENANT; public static final String REGIONAL_MICROSOFT_AUTHORITY_BASIC_HOST_WESTUS = "westus.login.microsoft.com"; - public static final String ARLINGTON_ORGANIZATIONS_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "organizations/"; - public static final String ARLINGTON_TENANT_SPECIFIC_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + ARLINGTON_AUTHORITY_TENANT; - public static final String ARLINGTON_COMMON_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "common/"; - public static final String ARLINGTON_GRAPH_DEFAULT_SCOPE = "https://graph.microsoft.us/.default"; - public static final String B2C_AUTHORITY = "https://msidlabb2c.b2clogin.com/msidlabb2c.onmicrosoft.com/"; public static final String B2C_AUTHORITY_LEGACY_FORMAT = "https://msidlabb2c.b2clogin.com/tfp/msidlabb2c.onmicrosoft.com/"; @@ -49,9 +40,11 @@ public class TestConstants { public static final String LOCALHOST = "http://localhost:"; - public static final String ADFS_AUTHORITY = "https://fs.msidlab8.com/adfs/"; public static final String ADFS_SCOPE = USER_READ_SCOPE; - public static final String ADFS_APP_ID = "PublicClientId"; public static final String AUTHORITY_PUBLIC_TENANT_SPECIFIC = "https://login.microsoftonline.com/" + MICROSOFT_AUTHORITY_TENANT; + + public static final String OBO_CLIENT_ID = "23c64cd8-21e4-41dd-9756-ab9e2c23f58c"; + public static final String OBO_APP_ID_URI = "api://23c64cd8-21e4-41dd-9756-ab9e2c23f58c"; + } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 94be0149..15924f0e 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -4,12 +4,8 @@ package com.microsoft.aad.msal4j; import com.microsoft.aad.msal4j.labapi2.*; -import com.microsoft.aad.msal4j.labapi2.Config; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Collections; @@ -18,14 +14,10 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class UsernamePasswordIT { - private Config cfg; - @Test void acquireTokenWithUsernamePassword_Managed() throws Exception { - cfg = new Config(); - LabResponse labResponse = LabUserHelper.getDefaultUser(); - assertAcquireTokenCommon(labResponse.getUser(), cfg.organizationsAuthority(), cfg.graphDefaultScope(), labResponse.getApp().getAppId()); + assertAcquireTokenCommon(labResponse.getUser(), TestConstants.ORGANIZATIONS_AUTHORITY, TestConstants.GRAPH_DEFAULT_SCOPE, labResponse.getApp().getAppId()); } @Test diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java deleted file mode 100644 index 65f42971..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AppCredentialProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi2; - -public class AppCredentialProvider { - - private final String clientId; - private final String oboClientId; - private final String oboAppPassword; - private final String oboAppIdURI; - - AppCredentialProvider() { - KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - - clientId = "54a2d933-8bf8-483b-a8f8-0a31924f3c1f"; - oboClientId = "23c64cd8-21e4-41dd-9756-ab9e2c23f58c"; - oboAppIdURI = "api://23c64cd8-21e4-41dd-9756-ab9e2c23f58c"; - oboAppPassword = keyVaultSecretsProvider.getSecretByName("IdentityDivisionDotNetOBOServiceSecret").getValue(); - } - - public String getAppId() { - return clientId; - } - - public String getOboAppId() { - return oboClientId; - } - - public String getOboAppPassword() { - return oboAppPassword; - } - - public String getOboAppIdURI() { - return oboAppIdURI; - } -} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java deleted file mode 100644 index 15a87090..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Config.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi2; - -public class Config { - private final String commonAuthority; - private final String organizationsAuthority; - private final String graphDefaultScope; - private final AppCredentialProvider appProvider; - private final String tenant; - - String azureEnvironment; - - public Config() { - this.azureEnvironment = "azurecloud"; - commonAuthority = LabConstants.COMMON_AUTHORITY; - organizationsAuthority = LabConstants.ORGANIZATIONS_AUTHORITY; - graphDefaultScope = LabConstants.GRAPH_DEFAULT_SCOPE; - appProvider = new AppCredentialProvider(); - tenant = LabConstants.MICROSOFT_AUTHORITY_TENANT; - - } - - public String commonAuthority() { - return this.commonAuthority; - } - - public String organizationsAuthority() { - return this.organizationsAuthority; - } - - public String graphDefaultScope() { - return this.graphDefaultScope; - } - - public String tenantSpecificAuthority() { - return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; - } - - public String tenantSpecificAuthority(String tenant) { - return LabConstants.MICROSOFT_AUTHORITY_HOST + tenant; - } - - public AppCredentialProvider appProvider() { - return appProvider; - } -} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java new file mode 100644 index 00000000..dbf219ba --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java @@ -0,0 +1,22 @@ +package com.microsoft.aad.msal4j.labapi2; + +public class KeyVaultRegistry { + private static final KeyVaultSecretsProvider MSID_LAB_PROVIDER = + new KeyVaultSecretsProvider(KeyVaultSecretsProvider.KeyVaultInstance.MSID_LAB); + + private static final KeyVaultSecretsProvider MSAL_TEAM_PROVIDER = + new KeyVaultSecretsProvider(KeyVaultSecretsProvider.KeyVaultInstance.MSAL_TEAM); + + public static KeyVaultSecretsProvider getMsidLabProvider() { + return MSID_LAB_PROVIDER; + } + + public static KeyVaultSecretsProvider getMsalTeamProvider() { + return MSAL_TEAM_PROVIDER; + } + + private KeyVaultRegistry() { + // Prevent instantiation + } +} + diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java index 76f0c934..713e645d 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java @@ -5,6 +5,8 @@ import com.azure.core.credential.AccessToken; import com.azure.core.credential.TokenCredential; +import com.azure.json.JsonProviders; +import com.azure.json.JsonReader; import com.azure.security.keyvault.secrets.SecretClient; import com.azure.security.keyvault.secrets.SecretClientBuilder; import com.azure.security.keyvault.secrets.models.KeyVaultSecret; @@ -154,6 +156,157 @@ IClientCredential getClientCredentialFromKeyStore() { return ClientCredentialFactory.createFromCertificate(key, publicCertificate); } + /** + * Get lab data from Key Vault by secret name. + * Automatically deserializes JSON content to LabResponse if valid JSON. + * + * @param secretName The Key Vault secret name + * @return Either LabResponse (if JSON) or String (if raw text) + */ + public Object getLabData(String secretName) { + try { + log.info("Retrieving Key Vault secret: {}", secretName); + KeyVaultSecret keyVaultSecret = getSecretByName(secretName); + String labData = keyVaultSecret.getValue(); + + if (labData == null || labData.isEmpty()) { + log.error("Key Vault secret '{}' is empty", secretName); + throw new LabUserNotFoundException(new UserQuery(), + "Found no content for secret '" + secretName + "' in Key Vault."); + } + + // Check if the value is JSON + if (isValidJson(labData)) { + LabResponse response; + try (JsonReader jsonReader = JsonProviders.createReader(labData)) { + response = LabResponse.fromJson(jsonReader); + } + + if (response == null) { + log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secretName); + throw new LabUserNotFoundException(new UserQuery(), + "Failed to deserialize Key Vault secret '" + secretName + "' to LabResponse."); + } + + log.debug("Retrieved LabResponse from Key Vault '{}': {}", secretName, + response.getUser() != null ? response.getUser().getUpn() : + response.getApp() != null ? response.getApp().getAppId() : "Unknown"); + return response; + } else { + log.debug("Retrieved raw string from Key Vault '{}': {} characters", secretName, labData.length()); + return labData; + } + } catch (Exception e) { + log.error("Failed to retrieve Key Vault secret '{}': {}", secretName, e.getMessage()); + throw new RuntimeException( + "Failed to retrieve or parse Key Vault secret '" + secretName + "'", e); + } + } + + /** + * Merge multiple Key Vault secrets into a single LabResponse. + * Each secret should contain a LabResponse JSON object. + * Fields from later secrets override fields from earlier ones. + * + * @param secretNames Array of Key Vault secret names to merge + * @return Merged LabResponse + */ + public LabResponse mergeLabResponses(String... secretNames) { + if (secretNames == null || secretNames.length == 0) { + throw new IllegalArgumentException( + "At least one secret name must be provided."); + } + + try { + LabResponse mergedResponse = new LabResponse(); + boolean hasValidResponse = false; + + for (String secretName : secretNames) { + Object data = getLabData(secretName); + + if (data instanceof LabResponse) { + LabResponse response = (LabResponse) data; + hasValidResponse = true; + + // Merge user, app, and lab fields (later values override earlier ones) + if (response.getUser() != null) { + mergedResponse.setUser(response.getUser()); + } + if (response.getApp() != null) { + mergedResponse.setApp(response.getApp()); + } + if (response.getLab() != null) { + mergedResponse.setLab(response.getLab()); + } + } + } + + if (!hasValidResponse) { + log.error("Failed to merge secrets - no valid LabResponse found: {}", + String.join(", ", secretNames)); + throw new LabUserNotFoundException(new UserQuery(), + "Failed to create merged LabResponse from secrets: " + + String.join(", ", secretNames)); + } + + log.info("Merged secrets [{}]: {}", String.join(", ", secretNames), + mergedResponse.getUser() != null ? mergedResponse.getUser().getUpn() : "N/A"); + + return mergedResponse; + } catch (Exception e) { + log.error("Failed to merge secrets [{}]: {}", String.join(", ", secretNames), e.getMessage()); + throw new RuntimeException( + "Failed to merge Key Vault secrets: " + String.join(", ", secretNames), e); + } + } + + /** + * Fetch user password from Key Vault. + * Note: This should only be called on instances configured for the MSID Lab vault. + * + * @param userLabName The lab name of the user (used as secret name) + * @return The user's password + */ + public String getUserPassword(String userLabName) { + if (userLabName == null || userLabName.trim().isEmpty()) { + log.error("Password fetch failed: empty lab name"); + throw new IllegalArgumentException( + "Error: lab name is not set on user. Password retrieval failed."); + } + + try { + log.debug("Fetching user password from Key Vault for: {}", userLabName); + KeyVaultSecret keyVaultSecret = getSecretByName(userLabName); + String password = keyVaultSecret.getValue(); + + if (password != null && !password.isEmpty()) { + log.debug("Password retrieved for user: {} ({} characters)", userLabName, password.length()); + return password; + } + + log.error("Password empty for user: {}", userLabName); + throw new IllegalStateException( + "Password secret '" + userLabName + "' found but was empty in Key Vault."); + } catch (Exception e) { + log.error("Password fetch failed for user {}: {}", userLabName, e.getMessage()); + throw new RuntimeException( + "Test setup: cannot get the user password from Key Vault secret '" + + userLabName + "'", e); + } + } + + /** + * Check if a string is valid JSON. + */ + private static boolean isValidJson(String value) { + try (JsonReader jsonReader = JsonProviders.createReader(value)) { + jsonReader.nextToken(); + return true; + } catch (Exception e) { + return false; + } + } + @Override public void close() { // Cleanup if needed diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java index 82fedc58..e429bd1e 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java @@ -23,13 +23,7 @@ class LabConstants { static final String AZURE_ENVIRONMENT = "azureenvironment"; static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; - static final String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; - - static final String COMMON_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "common/"; - static final String ORGANIZATIONS_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "organizations/"; static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; - static final String GRAPH_DEFAULT_SCOPE = "https://graph.windows.net/.default"; - static final String LAB_API_SCOPE = "https://request.msidlab.com/.default"; } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java index eb2014f2..a46e2905 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java @@ -17,7 +17,6 @@ class LabHttpHelper { private static final Logger log = LoggerFactory.getLogger(LabHttpHelper.class); - static String sendRequestToLab(String url, Map queryMap, String accessToken) throws IOException { return sendRequestToLab(buildUrl(url, queryMap), accessToken); } @@ -28,7 +27,6 @@ static String sendRequestToLab(String url, String accessToken) throws IOExceptio static String sendRequestToLab(URL labUrl, String accessToken) throws IOException { log.debug("Sending HTTP GET request to: {}", labUrl); - System.out.println(labUrl); HttpsURLConnection conn = (HttpsURLConnection) labUrl.openConnection(); diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java index 9ba207f9..9a837dfc 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java @@ -18,16 +18,45 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Wrapper for lab service API interactions. */ -public class LabServiceApi { +class LabServiceApi { private static final Logger log = LoggerFactory.getLogger(LabServiceApi.class); private ConfidentialClientApplication labApp; + private final ConcurrentHashMap userCache = new ConcurrentHashMap<>(); + /** + * Get lab user data with caching support. + * + * @param query The UserQuery to search for + * @return LabResponse + */ + LabResponse getLabUserData(UserQuery query) { + if (userCache.containsKey(query)) { + LabResponse cached = userCache.get(query); + log.debug("Lab cache hit: {}", + cached.getUser() != null ? cached.getUser().getUpn() : "N/A"); + return cached; + } + + LabResponse response = getLabResponseFromApi(query); + + if (response == null) { + log.error("No lab user found for query"); + throw new LabUserNotFoundException(query, "Found no users for the given query."); + } + + log.info("Lab API returned user: {}", + response.getUser() != null ? response.getUser().getUpn() : "N/A"); + + userCache.put(query, response); + return response; + } /** * Returns a test user account for use in testing. @@ -35,7 +64,7 @@ public class LabServiceApi { * @param query Any and all parameters that the returned user should satisfy * @return LabResponse with user that matches the query */ - LabResponse getLabResponseFromApi(UserQuery query) { + private LabResponse getLabResponseFromApi(UserQuery query) { log.debug("Querying lab API for user with parameters: {}", query); try { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java index 0fc95d9e..f9619348 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java @@ -145,8 +145,7 @@ public String getTenantId() { */ public String getPassword() { if (password == null || password.isEmpty()) { - // Fetch from MSID Lab Key Vault - password = LabUserHelper.fetchUserPassword(labName); + password = KeyVaultRegistry.getMsidLabProvider().getUserPassword(getLabName()); } return password; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java index edd9b9db..5cb8df40 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java @@ -3,109 +3,9 @@ package com.microsoft.aad.msal4j.labapi2; -import com.azure.json.JsonProviders; -import com.azure.json.JsonReader; -import com.azure.security.keyvault.secrets.models.KeyVaultSecret; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.ConcurrentHashMap; - public class LabUserHelper { - private static final Logger log = LoggerFactory.getLogger(LabUserHelper.class); private static final LabServiceApi labService = new LabServiceApi(); - private static final ConcurrentHashMap userCache = - new ConcurrentHashMap<>(); - - private static final KeyVaultSecretsProvider keyVaultSecretsProviderMsal; - private static final KeyVaultSecretsProvider keyVaultSecretsProviderMsid; - - static { - // MSAL Team vault - for configuration data - keyVaultSecretsProviderMsal = new KeyVaultSecretsProvider( - KeyVaultSecretsProvider.KeyVaultInstance.MSAL_TEAM); - - // MSID Lab vault - for user passwords - keyVaultSecretsProviderMsid = new KeyVaultSecretsProvider( - KeyVaultSecretsProvider.KeyVaultInstance.MSID_LAB); - } - - /** - * Get lab user data with caching support. - * - * @param query The UserQuery to search for - * @return LabResponse - */ - static LabResponse getLabUserData(UserQuery query) { - if (userCache.containsKey(query)) { - LabResponse cached = userCache.get(query); - log.debug("Lab cache hit: {}", - cached.getUser() != null ? cached.getUser().getUpn() : "N/A"); - return cached; - } - - LabResponse response = labService.getLabResponseFromApi(query); - - if (response == null) { - log.error("No lab user found for query"); - throw new LabUserNotFoundException(query, "Found no users for the given query."); - } - - log.info("Lab API returned user: {}", - response.getUser() != null ? response.getUser().getUpn() : "N/A"); - - userCache.put(query, response); - return response; - } - - /** - * Get lab data from Key Vault by secret name. - * Uses the MSAL Team vault for configuration data. - * - * @param secret The Key Vault secret name - * @return Either LabResponse or String - */ - private static Object getKVLabData(String secret) { - try { - log.info("Retrieving Key Vault secret: {}", secret); - // Use MSAL Team vault for configuration - KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsal.getSecretByName(secret); - String labData = keyVaultSecret.getValue(); - - if (labData == null || labData.isEmpty()) { - log.error("Key Vault secret '{}' is empty", secret); - throw new LabUserNotFoundException(new UserQuery(), - "Found no content for secret '" + secret + "' in Key Vault."); - } - - // Check if the value is JSON - if (isValidJson(labData)) { - LabResponse response; - try (JsonReader jsonReader = JsonProviders.createReader(labData)) { - response = LabResponse.fromJson(jsonReader); - } - - if (response == null) { - log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secret); - throw new LabUserNotFoundException(new UserQuery(), - "Failed to deserialize Key Vault secret '" + secret + "' to LabResponse."); - } - - log.debug("Retrieved LabResponse from Key Vault '{}': {}", secret, - response.getUser() != null ? response.getUser().getUpn() : - response.getApp() != null ? response.getApp().getAppId() : "Unknown"); - return response; - } else { - log.debug("Retrieved raw string from Key Vault '{}': {} characters", secret, labData.length()); - return labData; - } - } catch (Exception e) { - log.error("Failed to retrieve Key Vault secret '{}': {}", secret, e.getMessage()); - throw new RuntimeException( - "Failed to retrieve or parse Key Vault secret '" + secret + "'", e); - } - } /** * Fetch user password from Key Vault. @@ -115,143 +15,40 @@ private static Object getKVLabData(String secret) { * @return The user's password */ static String fetchUserPassword(String userLabName) { - if (userLabName == null || userLabName.trim().isEmpty()) { - log.error("Password fetch failed: empty lab name"); - throw new IllegalArgumentException( - "Error: lab name is not set on user. Password retrieval failed."); - } - - if (keyVaultSecretsProviderMsid == null || keyVaultSecretsProviderMsal == null) { - log.error("Password fetch failed: KeyVault provider not initialized"); - throw new IllegalStateException("Error: KeyVault secrets provider is not set"); - } - - try { - log.debug("Fetching user password from MSID Lab Key Vault for: {}", userLabName); - // Use MSID vault for passwords, not MSAL vault - KeyVaultSecret keyVaultSecret = keyVaultSecretsProviderMsid.getSecretByName(userLabName); - String password = keyVaultSecret.getValue(); - - if (password != null && !password.isEmpty()) { - log.debug("Password retrieved for user: {} ({} characters)", userLabName, password.length()); - return password; - } - - log.error("Password empty for user: {}", userLabName); - throw new IllegalStateException( - "Password secret '" + userLabName + "' found but was empty in Key Vault."); - } catch (Exception e) { - log.error("Password fetch failed for user {}: {}", userLabName, e.getMessage()); - throw new RuntimeException( - "Test setup: cannot get the user password from Key Vault secret '" + - userLabName + "'", e); - } - } - - /** - * Merge multiple Key Vault secrets into a single LabResponse. - * Each secret should contain a LabResponse JSON object. - * Fields from later secrets override fields from earlier ones. - * - * @param secrets Array of Key Vault secret names to merge - * @return Merged LabResponse - */ - static LabResponse mergeKVLabData(String... secrets) { - if (secrets == null || secrets.length == 0) { - throw new IllegalArgumentException( - "At least one secret name must be provided."); - } - - try { - LabResponse mergedResponse = new LabResponse(); - boolean hasValidResponse = false; - - for (String secret : secrets) { - - Object data = getKVLabData(secret); - - if (data instanceof LabResponse) { - LabResponse response = (LabResponse) data; - hasValidResponse = true; - - // Merge user, app, and lab fields (later values override earlier ones) - if (response.getUser() != null) { - mergedResponse.setUser(response.getUser()); - } - if (response.getApp() != null) { - mergedResponse.setApp(response.getApp()); - } - if (response.getLab() != null) { - mergedResponse.setLab(response.getLab()); - } - } - } - - if (!hasValidResponse) { - log.error("Failed to merge secrets - no valid LabResponse found: {}", - String.join(", ", secrets)); - throw new LabUserNotFoundException(new UserQuery(), - "Failed to create merged LabResponse from secrets: " + - String.join(", ", secrets)); - } - - log.info("Merged secrets [{}]: {}", String.join(", ", secrets), - mergedResponse.getUser() != null ? mergedResponse.getUser().getUpn() : "N/A"); - - return mergedResponse; - } catch (Exception e) { - log.error("Failed to merge secrets [{}]: {}", String.join(", ", secrets), e.getMessage()); - throw new RuntimeException( - "Failed to merge Key Vault secrets: " + String.join(", ", secrets), e); - } - } - - /** - * Check if a string is valid JSON. - */ - private static boolean isValidJson(String value) { - try (JsonReader jsonReader = JsonProviders.createReader(value)) { - jsonReader.nextToken(); - return true; - } catch (Exception e) { - return false; - } + return KeyVaultRegistry.getMsidLabProvider().getUserPassword(userLabName); } public static LabResponse getDefaultUser() { - return mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); } public static LabResponse getDefaultUserMultiTenantApp() { - return mergeKVLabData("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgsPC-JSON"); + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgs-JSON"); + } + + //Used to avoid AADSTS7000218 credential errors in certain public client scenarios + public static LabResponse getDefaultUserMultiTenantAppPublicClient() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgsPC-JSON"); } public static LabResponse getDefaultAdfsUser() { - return mergeKVLabData("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); } public static LabResponse getB2CLocalAccount() { - return getLabUserData(UserQuery.b2cLocalAccountQuery()); + return labService.getLabUserData(UserQuery.b2cLocalAccountQuery()); } public static LabResponse getArlingtonUser() { // Query the Lab API with Arlington-specific parameters - return getLabUserData(UserQuery.arlingtonUserQuery()); - } - - static LabResponse getArlingtonADFSUser() { - // Create a modified query with federated user type - UserQuery query = UserQuery.arlingtonUserQuery(); - query.setUserType(LabServiceParameters.UserType.FEDERATED); - - return getLabUserData(query); + return labService.getLabUserData(UserQuery.arlingtonUserQuery()); } public static LabResponse getCiamCudUser() { - return getLabUserData(UserQuery.ciamCudUserQuery()); + return labService.getLabUserData(UserQuery.ciamCudUserQuery()); } public static LabResponse getMSAUser() { - return getLabUserData(UserQuery.msaUserQuery()); + return labService.getLabUserData(UserQuery.msaUserQuery()); } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java deleted file mode 100644 index 5e266911..00000000 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -//---------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -//------------------------------------------------------------------------------ - -package infrastructure; - -public class SeleniumConstants { - - static final String WEB_UPN_INPUT_ID = "i0116"; - static final String WEB_PASSWORD_ID = "i0118"; - static final String WEB_SUBMIT_ID = "idSIButton9"; - - //ADFS - static final String ADFS_UPN_INPUT_ID = "userNameInput"; - static final String ADFS_PASSWORD_ID = "passwordInput"; - static final String ADFS_SUBMIT_ID = "submitButton"; - - // B2C Local - static final String B2C_LOCAL_ACCOUNT_ID = "SignInWithLogonNameExchange"; - static final String B2C_LOCAL_USERNAME_ID = "cred_userid_inputtext"; - static final String B2C_LOCAL_PASSWORD_ID = "cred_password_inputtext"; - static final String B2C_LOCAL_SIGN_IN_BUTTON_ID = "cred_sign_in_button"; - - // Stay signed in? - static final String STAY_SIGN_IN_NO_BUTTON_ID = "idBtn_Back"; - - // Are you trying to sign in to ... - //Only continue if you downloaded the app from a store or website that you trust. - static final String ARE_YOU_TRYING_TO_SIGN_IN_TO = "idSIButton9"; -} diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index 593f64cb..752bdd62 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -5,9 +5,10 @@ import com.microsoft.aad.msal4j.TestConstants; import com.microsoft.aad.msal4j.labapi2.LabUser; +import infrastructure.pageobjects.ADFSLoginPage; +import infrastructure.pageobjects.AzureADLoginPage; +import infrastructure.pageobjects.B2CLocalLoginPage; import org.openqa.selenium.By; -import org.openqa.selenium.StaleElementReferenceException; -import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; @@ -23,9 +24,6 @@ public class SeleniumExtensions { private static final Logger LOG = LoggerFactory.getLogger(SeleniumExtensions.class); - private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); - private static final Duration COMMON_ELEMENT_TIMEOUT = Duration.ofSeconds(5); - private SeleniumExtensions() { } @@ -34,133 +32,69 @@ public static WebDriver createDefaultWebDriver() { options.addArguments("--headless"); options.addArguments("--incognito"); - System.setProperty("webdriver.chrome.driver", "C:/Windows/chromedriver.exe"); return new ChromeDriver(options); } public static WebElement waitForElementToBeVisibleAndEnabled(WebDriver driver, By by, Duration timeout) { WebDriverWait wait = new WebDriverWait(driver, timeout.getSeconds()); - return wait.until(dr -> { - try { - WebElement element = driver.findElement(by); - if (element.isDisplayed() && element.isEnabled()) { - return element; - } - return null; - } catch (StaleElementReferenceException e) { - LOG.debug("Stale element in waitForElementToBeVisibleAndEnabled: {}", e.getMessage()); - return null; - } - }); - } - - public static WebElement waitForElementToBeVisibleAndEnabled(WebDriver driver, By by) { - return waitForElementToBeVisibleAndEnabled(driver, by, DEFAULT_TIMEOUT); + return wait.until(ExpectedConditions.elementToBeClickable(by)); } public static void performADOrCiamLogin(WebDriver driver, LabUser user) { LOG.info("performADOrCiamLogin for user: {}", user.getUpn()); - UserInformationFields fields = new UserInformationFields(user); - - LOG.info("Entering username"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getAadUserNameInputId())) - .sendKeys(user.getUpn()); - - LOG.info("Clicking Next after username"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getAadSignInButtonId())) - .click(); - - LOG.info("Entering password"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())) - .sendKeys(user.getPassword()); - - LOG.info("Clicking submit"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordSigInButtonId())) - .click(); - - if (checkAuthenticationCompletePage(driver)) { - return; - } - - handleOptionalPrompts(driver); - } - - private static boolean checkAuthenticationCompletePage(WebDriver driver) { - try { - WebDriverWait wait = new WebDriverWait(driver, COMMON_ELEMENT_TIMEOUT.getSeconds()); - wait.until(ExpectedConditions.textToBePresentInElementLocated( - By.tagName("body"), "Authentication complete")); - return true; - } catch (TimeoutException ex) { - LOG.debug("Authentication complete page not found: {}", ex.getMessage()); - return false; - } - } - - private static void handleOptionalPrompts(WebDriver driver) { - // Handle "Are you trying to sign in to..." prompt - try { - LOG.info("Checking for 'Are you trying to sign in' prompt"); - waitForElementToBeVisibleAndEnabled( - driver, - By.id(SeleniumConstants.ARE_YOU_TRYING_TO_SIGN_IN_TO), - COMMON_ELEMENT_TIMEOUT) - .click(); - LOG.info("Clicked Continue on sign-in prompt"); - } catch (TimeoutException ex) { - LOG.debug("No 'Are you trying to sign in' prompt found"); - } - - // Handle "Stay signed in?" prompt - try { - LOG.info("Checking for 'Stay signed in' prompt"); - waitForElementToBeVisibleAndEnabled( - driver, - By.id(SeleniumConstants.STAY_SIGN_IN_NO_BUTTON_ID), - COMMON_ELEMENT_TIMEOUT) - .click(); - LOG.info("Clicked No on 'Stay signed in' prompt"); - } catch (TimeoutException ex) { - LOG.debug("No 'Stay signed in' prompt found"); - } + AzureADLoginPage loginPage = new AzureADLoginPage(driver); + loginPage.login(user.getUpn(), user.getPassword()); } public static void performADFSLogin(WebDriver driver, LabUser user) { LOG.info("performADFSLogin for user: {}", user.getUpn()); - UserInformationFields fields = new UserInformationFields(user); - - LOG.info("Entering username"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getADFSUserNameInputId())) - .sendKeys(user.getUpn()); - - LOG.info("Entering password"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordInputId())) - .sendKeys(user.getPassword()); - - LOG.info("Clicking submit"); - waitForElementToBeVisibleAndEnabled(driver, By.id(fields.getPasswordSigInButtonId())) - .click(); + ADFSLoginPage loginPage = new ADFSLoginPage(driver); + loginPage.login(user.getUpn(), user.getPassword()); } public static void performLocalLogin(WebDriver driver, LabUser user) { LOG.info("performLocalLogin"); - waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_ACCOUNT_ID)) - .click(); + B2CLocalLoginPage loginPage = new B2CLocalLoginPage(driver); + loginPage.login(TestConstants.B2C_UPN, user.getPassword()); + } - LOG.info("Entering username"); - waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_USERNAME_ID)) - .sendKeys(TestConstants.B2C_UPN); + /** + * Perform device code flow authentication. + * Navigates to the verification URI, enters the device code, and completes Azure AD login. + * + * @param driver The WebDriver instance + * @param verificationUri The URI to navigate to for device code entry + * @param userCode The device code to enter + * @param user The lab user credentials for login + */ + public static void performDeviceCodeLogin(WebDriver driver, String verificationUri, String userCode, LabUser user) { + LOG.info("performDeviceCodeLogin for user: {}", user.getUpn()); - LOG.info("Entering password"); - waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_PASSWORD_ID)) - .sendKeys(user.getPassword()); + try { + // Navigate to device code verification page + LOG.info("Navigating to verification URI"); + driver.navigate().to(verificationUri); + + // Enter device code + LOG.info("Entering device code"); + By deviceCodeInputField = By.id("otc"); + waitForElementToBeVisibleAndEnabled(driver, deviceCodeInputField, Duration.ofSeconds(15)) + .sendKeys(userCode); + + // Click continue button + LOG.info("Clicking continue button"); + By continueButton = By.id("idSIButton9"); + waitForElementToBeVisibleAndEnabled(driver, continueButton, Duration.ofSeconds(15)) + .click(); - LOG.info("Clicking sign in"); - waitForElementToBeVisibleAndEnabled(driver, By.id(SeleniumConstants.B2C_LOCAL_SIGN_IN_BUTTON_ID)) - .click(); + // Perform standard Azure AD login + performADOrCiamLogin(driver, user); + } catch (Exception e) { + LOG.error("Device code flow automation failed: {}", e.getMessage()); + throw new RuntimeException("Device code flow automation failed", e); + } } -} +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java b/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java deleted file mode 100644 index 62b92410..00000000 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package infrastructure; - -import com.microsoft.aad.msal4j.labapi2.LabUser; - -class UserInformationFields { - private final LabUser user; - private String passwordInputId; - private String passwordSigInButtonId; - - UserInformationFields(LabUser labUser) { - this.user = labUser; - } - - String getPasswordInputId() { - if (passwordInputId == null || passwordInputId.equals("")) { - determineFieldIds(); - } - return passwordInputId; - } - - String getPasswordSigInButtonId() { - if (passwordSigInButtonId == null || passwordSigInButtonId.equals("")) { - determineFieldIds(); - } - return passwordSigInButtonId; - } - - String getAadSignInButtonId() { - return SeleniumConstants.WEB_SUBMIT_ID; - } - - String getAadUserNameInputId() { - return SeleniumConstants.WEB_UPN_INPUT_ID; - } - - String getADFSUserNameInputId() { - return SeleniumConstants.ADFS_UPN_INPUT_ID; - } - - private void determineFieldIds() { - String federationProvider = user.getFederationProvider(); - - passwordInputId = SeleniumConstants.WEB_PASSWORD_ID; - passwordSigInButtonId = SeleniumConstants.WEB_SUBMIT_ID; - } -} diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/ADFSLoginPage.java b/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/ADFSLoginPage.java new file mode 100644 index 00000000..ec7ccd0a --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/ADFSLoginPage.java @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package infrastructure.pageobjects; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; + +/** + * Page Object Model for ADFS login page. + * Represents the Active Directory Federation Services authentication flow. + */ +public class ADFSLoginPage { + + private static final Logger LOG = LoggerFactory.getLogger(ADFSLoginPage.class); + + private final WebDriver driver; + private final WebDriverWait wait; + + // Element locators + private static final By USERNAME_INPUT = By.id("userNameInput"); + private static final By PASSWORD_INPUT = By.id("passwordInput"); + private static final By SUBMIT_BUTTON = By.id("submitButton"); + + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); + + public ADFSLoginPage(WebDriver driver) { + this.driver = driver; + this.wait = new WebDriverWait(driver, DEFAULT_TIMEOUT.getSeconds()); + } + + /** + * Enter the username in the ADFS login page. + * + * @param username The username/UPN to enter + * @return This page object for method chaining + */ + public ADFSLoginPage enterUsername(String username) { + LOG.info("Entering username: {}", username); + wait.until(ExpectedConditions.elementToBeClickable(USERNAME_INPUT)) + .sendKeys(username); + return this; + } + + /** + * Enter the password in the ADFS login page. + * + * @param password The password to enter + * @return This page object for method chaining + */ + public ADFSLoginPage enterPassword(String password) { + LOG.info("Entering password"); + wait.until(ExpectedConditions.elementToBeClickable(PASSWORD_INPUT)) + .sendKeys(password); + return this; + } + + /** + * Click the Submit button to complete login. + * + * @return This page object for method chaining + */ + public ADFSLoginPage clickSubmit() { + LOG.info("Clicking submit button"); + wait.until(ExpectedConditions.elementToBeClickable(SUBMIT_BUTTON)) + .click(); + return this; + } + + /** + * Perform a complete ADFS login flow. + * This is a convenience method that chains all the necessary steps. + * + * @param username The username/UPN + * @param password The password + */ + public void login(String username, String password) { + enterUsername(username) + .enterPassword(password) + .clickSubmit(); + + LOG.info("ADFS login completed"); + } +} diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/AzureADLoginPage.java b/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/AzureADLoginPage.java new file mode 100644 index 00000000..dcaab635 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/AzureADLoginPage.java @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package infrastructure.pageobjects; + +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; + +/** + * Page Object Model for Azure AD login page. + * Represents the standard Azure AD authentication flow. + */ +public class AzureADLoginPage { + + private static final Logger LOG = LoggerFactory.getLogger(AzureADLoginPage.class); + + private final WebDriver driver; + private final WebDriverWait wait; + + // Element locators + private static final By USERNAME_INPUT = By.id("i0116"); + private static final By PASSWORD_INPUT = By.id("i0118"); + private static final By NEXT_BUTTON = By.id("idSIButton9"); + private static final By SUBMIT_BUTTON = By.id("idSIButton9"); + + // Optional prompts + private static final By ARE_YOU_TRYING_TO_SIGN_IN_BUTTON = By.id("idSIButton9"); + private static final By STAY_SIGNED_IN_NO_BUTTON = By.id("idBtn_Back"); + + // Authentication complete page + private static final By AUTH_COMPLETE_BODY = By.tagName("body"); + private static final String AUTH_COMPLETE_TEXT = "Authentication complete"; + + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); + private static final Duration SHORT_TIMEOUT = Duration.ofSeconds(5); + + public AzureADLoginPage(WebDriver driver) { + this.driver = driver; + this.wait = new WebDriverWait(driver, DEFAULT_TIMEOUT.getSeconds()); + } + + /** + * Enter the username in the login page. + * + * @param username The username/UPN to enter + * @return This page object for method chaining + */ + public AzureADLoginPage enterUsername(String username) { + LOG.info("Entering username: {}", username); + wait.until(ExpectedConditions.elementToBeClickable(USERNAME_INPUT)) + .sendKeys(username); + return this; + } + + /** + * Click the Next button after entering username. + * + * @return This page object for method chaining + */ + public AzureADLoginPage clickNext() { + LOG.info("Clicking Next button"); + wait.until(ExpectedConditions.elementToBeClickable(NEXT_BUTTON)) + .click(); + return this; + } + + /** + * Enter the password in the login page. + * + * @param password The password to enter + * @return This page object for method chaining + */ + public AzureADLoginPage enterPassword(String password) { + LOG.info("Entering password"); + wait.until(ExpectedConditions.elementToBeClickable(PASSWORD_INPUT)) + .sendKeys(password); + return this; + } + + /** + * Click the Submit/Sign in button after entering password. + * + * @return This page object for method chaining + */ + public AzureADLoginPage clickSubmit() { + LOG.info("Clicking Submit button"); + wait.until(ExpectedConditions.elementToBeClickable(SUBMIT_BUTTON)) + .click(); + return this; + } + + /** + * Check if the authentication complete page is displayed. + * + * @return true if authentication is complete, false otherwise + */ + public boolean isAuthenticationComplete() { + try { + WebDriverWait shortWait = new WebDriverWait(driver, SHORT_TIMEOUT.getSeconds()); + shortWait.until(ExpectedConditions.textToBePresentInElementLocated( + AUTH_COMPLETE_BODY, AUTH_COMPLETE_TEXT)); + LOG.info("Authentication complete page detected"); + return true; + } catch (TimeoutException ex) { + LOG.debug("Authentication complete page not found"); + return false; + } + } + + /** + * Perform a complete Azure AD login flow. + * This is a convenience method that chains all the necessary steps. + * + * @param username The username/UPN + * @param password The password + */ + public void login(String username, String password) { + enterUsername(username) + .clickNext() + .enterPassword(password) + .clickSubmit(); + + if (isAuthenticationComplete()) { + LOG.info("Authentication completed successfully"); + return; + } + + handleOptionalPrompts(); + } + + /** + * Handle optional prompts that may appear after login. + * These include "Are you trying to sign in to..." and "Stay signed in?" prompts. + */ + private void handleOptionalPrompts() { + handleAreYouTryingToSignInPrompt(); + handleStaySignedInPrompt(); + } + + /** + * Handle the "Are you trying to sign in to..." prompt if it appears. + */ + private void handleAreYouTryingToSignInPrompt() { + try { + LOG.info("Checking for 'Are you trying to sign in' prompt"); + WebDriverWait shortWait = new WebDriverWait(driver, SHORT_TIMEOUT.getSeconds()); + shortWait.until(ExpectedConditions.elementToBeClickable(ARE_YOU_TRYING_TO_SIGN_IN_BUTTON)) + .click(); + LOG.info("Clicked Continue on 'Are you trying to sign in' prompt"); + } catch (TimeoutException ex) { + LOG.debug("No 'Are you trying to sign in' prompt found"); + } + } + + /** + * Handle the "Stay signed in?" prompt if it appears. + */ + private void handleStaySignedInPrompt() { + try { + LOG.info("Checking for 'Stay signed in' prompt"); + WebDriverWait shortWait = new WebDriverWait(driver, SHORT_TIMEOUT.getSeconds()); + shortWait.until(ExpectedConditions.elementToBeClickable(STAY_SIGNED_IN_NO_BUTTON)) + .click(); + LOG.info("Clicked No on 'Stay signed in' prompt"); + } catch (TimeoutException ex) { + LOG.debug("No 'Stay signed in' prompt found"); + } + } +} diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/B2CLocalLoginPage.java b/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/B2CLocalLoginPage.java new file mode 100644 index 00000000..8b40c138 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/pageobjects/B2CLocalLoginPage.java @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package infrastructure.pageobjects; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; + +/** + * Page Object Model for B2C Local Account login page. + * Represents the Azure AD B2C local account authentication flow. + */ +public class B2CLocalLoginPage { + + private static final Logger LOG = LoggerFactory.getLogger(B2CLocalLoginPage.class); + + private final WebDriver driver; + private final WebDriverWait wait; + + // Element locators + private static final By LOCAL_ACCOUNT_BUTTON = By.id("SignInWithLogonNameExchange"); + private static final By USERNAME_INPUT = By.id("cred_userid_inputtext"); + private static final By PASSWORD_INPUT = By.id("cred_password_inputtext"); + private static final By SIGN_IN_BUTTON = By.id("cred_sign_in_button"); + + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(15); + + public B2CLocalLoginPage(WebDriver driver) { + this.driver = driver; + this.wait = new WebDriverWait(driver, DEFAULT_TIMEOUT.getSeconds()); + } + + /** + * Click the local account sign-in option. + * + * @return This page object for method chaining + */ + public B2CLocalLoginPage clickLocalAccount() { + LOG.info("Clicking local account button"); + wait.until(ExpectedConditions.elementToBeClickable(LOCAL_ACCOUNT_BUTTON)) + .click(); + return this; + } + + /** + * Enter the username in the B2C login page. + * + * @param username The username to enter + * @return This page object for method chaining + */ + public B2CLocalLoginPage enterUsername(String username) { + LOG.info("Entering username: {}", username); + wait.until(ExpectedConditions.elementToBeClickable(USERNAME_INPUT)) + .sendKeys(username); + return this; + } + + /** + * Enter the password in the B2C login page. + * + * @param password The password to enter + * @return This page object for method chaining + */ + public B2CLocalLoginPage enterPassword(String password) { + LOG.info("Entering password"); + wait.until(ExpectedConditions.elementToBeClickable(PASSWORD_INPUT)) + .sendKeys(password); + return this; + } + + /** + * Click the Sign in button to complete login. + * + * @return This page object for method chaining + */ + public B2CLocalLoginPage clickSignIn() { + LOG.info("Clicking sign in button"); + wait.until(ExpectedConditions.elementToBeClickable(SIGN_IN_BUTTON)) + .click(); + return this; + } + + /** + * Perform a complete B2C local account login flow. + * This is a convenience method that chains all the necessary steps. + * + * @param username The username + * @param password The password + */ + public void login(String username, String password) { + clickLocalAccount() + .enterUsername(username) + .enterPassword(password) + .clickSignIn(); + + LOG.info("B2C local login completed"); + } +} From dde726094db5d9ee9ada2be3d589005b06b8b796 Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 24 Nov 2025 15:07:48 -0800 Subject: [PATCH 11/14] Final cleanup, remove old labapi package and rename the new labapi2 package --- .../AcquireTokenInteractiveIT.java | 2 +- .../AcquireTokenSilentIT.java | 2 +- .../AuthorizationCodeIT.java | 2 +- .../AzureEnvironmentIT.java | 2 +- .../CertificateHelper.java | 2 +- .../ClientCredentialsIT.java | 2 +- .../DeviceCodeIT.java | 2 +- .../HttpClientIT.java | 2 +- .../OnBehalfOfIT.java | 2 +- .../RefreshTokenIT.java | 2 +- .../SeleniumTest.java | 2 +- .../TokenCacheIT.java | 2 +- .../UsernamePasswordIT.java | 2 +- .../{labapi2 => labapi}/AzureEnvironment.java | 3 +- .../{labapi2 => labapi}/KeyVaultRegistry.java | 5 +- .../KeyVaultSecretsProvider.java | 18 +- .../microsoft/aad/msal4j}/labapi/Lab.java | 2 +- .../LabApiService.java} | 22 +-- .../msal4j/{labapi2 => labapi}/LabApp.java | 2 +- .../{labapi2 => labapi}/LabConstants.java | 2 +- .../{labapi2 => labapi}/LabHttpHelper.java | 5 +- .../{labapi2 => labapi}/LabResponse.java | 2 +- .../LabServiceParameters.java | 2 +- .../msal4j/{labapi2 => labapi}/LabUser.java | 14 +- .../{labapi2 => labapi}/LabUserHelper.java | 26 +-- .../LabUserNotFoundException.java | 6 +- .../UserQueryHelper.java} | 37 ++-- .../com/microsoft/aad/msal4j/labapi2/Lab.java | 76 -------- .../infrastructure/SeleniumExtensions.java | 2 +- .../src/integrationtest/java/labapi/App.java | 87 --------- .../java/labapi/AppCredentialProvider.java | 66 ------- .../java/labapi/AzureEnvironment.java | 12 -- .../java/labapi/B2CProvider.java | 10 - .../java/labapi/FederationProvider.java | 14 -- .../java/labapi/HttpClientHelper.java | 68 ------- .../java/labapi/KeyVaultSecretsProvider.java | 110 ----------- .../java/labapi/LabConstants.java | 27 --- .../java/labapi/LabService.java | 135 -------------- .../java/labapi/LabUserProvider.java | 118 ------------ .../src/integrationtest/java/labapi/User.java | 171 ------------------ .../java/labapi/UserQueryParameters.java | 20 -- .../java/labapi/UserSecret.java | 53 ------ .../integrationtest/java/labapi/UserType.java | 12 -- 43 files changed, 84 insertions(+), 1069 deletions(-) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/AzureEnvironment.java (81%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/KeyVaultRegistry.java (82%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/KeyVaultSecretsProvider.java (97%) rename msal4j-sdk/src/integrationtest/java/{ => com/microsoft/aad/msal4j}/labapi/Lab.java (98%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2/LabServiceApi.java => labapi/LabApiService.java} (94%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabApp.java (98%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabConstants.java (96%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabHttpHelper.java (95%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabResponse.java (98%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabServiceParameters.java (92%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabUser.java (94%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabUserHelper.java (56%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2 => labapi}/LabUserNotFoundException.java (75%) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/{labapi2/UserQuery.java => labapi/UserQueryHelper.java} (76%) delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/App.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/AppCredentialProvider.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/AzureEnvironment.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/B2CProvider.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/FederationProvider.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/HttpClientHelper.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/KeyVaultSecretsProvider.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/LabConstants.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/LabService.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/LabUserProvider.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/User.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/UserQueryParameters.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/UserSecret.java delete mode 100644 msal4j-sdk/src/integrationtest/java/labapi/UserType.java diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index d549074a..483a1fef 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index 2ef840f8..99e4c689 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index 2ef9b3eb..0c1faaa0 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java index 1f28030e..6f3e9170 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/CertificateHelper.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/CertificateHelper.java index 7b87a54b..2b572329 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/CertificateHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/CertificateHelper.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import labapi.KeyVaultSecretsProvider; +import com.microsoft.aad.msal4j.labapi.KeyVaultSecretsProvider; import org.apache.commons.lang3.SystemUtils; import java.io.IOException; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java index 504aa4ea..283b5bfc 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.BeforeAll; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index a4cb006c..eef7288d 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import infrastructure.SeleniumExtensions; import org.openqa.selenium.WebDriver; import org.slf4j.Logger; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java index 2b891b46..71173911 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java index fb45738d..3612b4b9 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java index 3e7fce67..2e0d2e5a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java index a1a6def8..4f50b907 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.LabUser; +import com.microsoft.aad.msal4j.labapi.LabUser; import infrastructure.SeleniumExtensions; import org.openqa.selenium.WebDriver; import org.slf4j.Logger; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index bff8b11f..7f7ca005 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index 15924f0e..fb466c9d 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi2.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java similarity index 81% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java index 55d9e032..f4881d54 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/AzureEnvironment.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; class AzureEnvironment { - static final String AZURE_US_GOVERNMENT = "azureusgovernment"; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java similarity index 82% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java index dbf219ba..d1cf81ec 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultRegistry.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java @@ -1,4 +1,7 @@ -package com.microsoft.aad.msal4j.labapi2; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi; public class KeyVaultRegistry { private static final KeyVaultSecretsProvider MSID_LAB_PROVIDER = diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java similarity index 97% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java index 713e645d..b80eae4c 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/KeyVaultSecretsProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; import com.azure.core.credential.AccessToken; import com.azure.core.credential.TokenCredential; @@ -26,6 +26,11 @@ public class KeyVaultSecretsProvider implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(KeyVaultSecretsProvider.class); + /** + * The certificate alias used for authentication with Key Vault. + */ + public static final String CERTIFICATE_ALIAS = "LabAuth.MSIDLab.com"; + static class KeyVaultInstance { /** * The KeyVault maintained by the MSID. It is recommended for use. @@ -145,8 +150,8 @@ IClientCredential getClientCredentialFromKeyStore() { } keystore.load(null, null); - key = (PrivateKey) keystore.getKey("LabAuth.MSIDLab.com", null); - publicCertificate = (X509Certificate) keystore.getCertificate("LabAuth.MSIDLab.com"); + key = (PrivateKey) keystore.getKey(CERTIFICATE_ALIAS, null); + publicCertificate = (X509Certificate) keystore.getCertificate(CERTIFICATE_ALIAS); log.debug("Successfully loaded client certificate from keystore"); } catch (Exception e) { @@ -171,7 +176,7 @@ public Object getLabData(String secretName) { if (labData == null || labData.isEmpty()) { log.error("Key Vault secret '{}' is empty", secretName); - throw new LabUserNotFoundException(new UserQuery(), + throw new LabUserNotFoundException(new UserQueryHelper(), "Found no content for secret '" + secretName + "' in Key Vault."); } @@ -184,7 +189,7 @@ public Object getLabData(String secretName) { if (response == null) { log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secretName); - throw new LabUserNotFoundException(new UserQuery(), + throw new LabUserNotFoundException(new UserQueryHelper(), "Failed to deserialize Key Vault secret '" + secretName + "' to LabResponse."); } @@ -244,7 +249,7 @@ public LabResponse mergeLabResponses(String... secretNames) { if (!hasValidResponse) { log.error("Failed to merge secrets - no valid LabResponse found: {}", String.join(", ", secretNames)); - throw new LabUserNotFoundException(new UserQuery(), + throw new LabUserNotFoundException(new UserQueryHelper(), "Failed to create merged LabResponse from secrets: " + String.join(", ", secretNames)); } @@ -309,6 +314,5 @@ private static boolean isValidJson(String value) { @Override public void close() { - // Cleanup if needed } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/Lab.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/Lab.java similarity index 98% rename from msal4j-sdk/src/integrationtest/java/labapi/Lab.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/Lab.java index 5712d62d..a2100468 100644 --- a/msal4j-sdk/src/integrationtest/java/labapi/Lab.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/Lab.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package labapi; +package com.microsoft.aad.msal4j.labapi; import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java similarity index 94% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java index 9a837dfc..6fb8aee8 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceApi.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java @@ -1,15 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; import com.azure.json.JsonProviders; import com.azure.json.JsonReader; import com.microsoft.aad.msal4j.ClientCredentialParameters; import com.microsoft.aad.msal4j.ConfidentialClientApplication; import com.microsoft.aad.msal4j.IAuthenticationResult; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.MFA; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.ProtectionPolicy; +import com.microsoft.aad.msal4j.labapi.LabServiceParameters.MFA; +import com.microsoft.aad.msal4j.labapi.LabServiceParameters.ProtectionPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,20 +23,20 @@ /** * Wrapper for lab service API interactions. */ -class LabServiceApi { +class LabApiService { - private static final Logger log = LoggerFactory.getLogger(LabServiceApi.class); + private static final Logger log = LoggerFactory.getLogger(LabApiService.class); private ConfidentialClientApplication labApp; - private final ConcurrentHashMap userCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap userCache = new ConcurrentHashMap<>(); /** * Get lab user data with caching support. * - * @param query The UserQuery to search for + * @param query The UserQueryHelper to search for * @return LabResponse */ - LabResponse getLabUserData(UserQuery query) { + LabResponse getLabUserData(UserQueryHelper query) { if (userCache.containsKey(query)) { LabResponse cached = userCache.get(query); log.debug("Lab cache hit: {}", @@ -64,7 +64,7 @@ LabResponse getLabUserData(UserQuery query) { * @param query Any and all parameters that the returned user should satisfy * @return LabResponse with user that matches the query */ - private LabResponse getLabResponseFromApi(UserQuery query) { + private LabResponse getLabResponseFromApi(UserQueryHelper query) { log.debug("Querying lab API for user with parameters: {}", query); try { @@ -161,10 +161,10 @@ LabResponse createLabResponseFromResultString(String result) { /** * Execute a query against the lab API. * - * @param query UserQuery parameters + * @param query UserQueryHelper parameters * @return JSON response string */ - private String runQuery(UserQuery query) { + private String runQuery(UserQueryHelper query) { Map queryDict = new HashMap<>(); // Building user query - required parameters set to defaults if not supplied diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApp.java similarity index 98% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApp.java index 736a3e04..6b55c6d1 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabApp.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApp.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java similarity index 96% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java index e429bd1e..a93e64b5 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabConstants.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; /** * Constants for lab API endpoints and query parameters. diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java similarity index 95% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java index a46e2905..f7f0b170 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabHttpHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java @@ -1,4 +1,7 @@ -package com.microsoft.aad.msal4j.labapi2; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java similarity index 98% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java index 2f0c2c66..d7ce82cf 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabResponse.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java similarity index 92% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java index 01b3bade..cca6e759 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabServiceParameters.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; class LabServiceParameters { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUser.java similarity index 94% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUser.java index f9619348..0f2fe0cb 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUser.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUser.java @@ -1,4 +1,7 @@ -package com.microsoft.aad.msal4j.labapi2; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi; import com.azure.json.JsonReader; import com.azure.json.JsonSerializable; @@ -149,13 +152,4 @@ public String getPassword() { } return password; } - - /** - * Get the federation provider for this user. - * - * @return The federation provider string (e.g., "adfsv2019", "adfsv4", "none") - */ - public String getFederationProvider() { - return federationProvider != null ? federationProvider : "none"; - } } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java similarity index 56% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java index 5cb8df40..708b9f78 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserHelper.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java @@ -1,22 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; public class LabUserHelper { - private static final LabServiceApi labService = new LabServiceApi(); - - /** - * Fetch user password from Key Vault. - * Uses the MSID Lab vault (different from configuration vault). - * - * @param userLabName The lab name of the user (used as secret name) - * @return The user's password - */ - static String fetchUserPassword(String userLabName) { - return KeyVaultRegistry.getMsidLabProvider().getUserPassword(userLabName); - } + private static final LabApiService labService = new LabApiService(); public static LabResponse getDefaultUser() { return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); @@ -26,7 +15,7 @@ public static LabResponse getDefaultUserMultiTenantApp() { return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgs-JSON"); } - //Used to avoid AADSTS7000218 credential errors in certain public client scenarios + //Avoids AADSTS7000218 credential errors in certain public client scenarios public static LabResponse getDefaultUserMultiTenantAppPublicClient() { return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgsPC-JSON"); } @@ -36,19 +25,18 @@ public static LabResponse getDefaultAdfsUser() { } public static LabResponse getB2CLocalAccount() { - return labService.getLabUserData(UserQuery.b2cLocalAccountQuery()); + return labService.getLabUserData(UserQueryHelper.b2cLocalAccountQuery()); } public static LabResponse getArlingtonUser() { - // Query the Lab API with Arlington-specific parameters - return labService.getLabUserData(UserQuery.arlingtonUserQuery()); + return labService.getLabUserData(UserQueryHelper.arlingtonUserQuery()); } public static LabResponse getCiamCudUser() { - return labService.getLabUserData(UserQuery.ciamCudUserQuery()); + return labService.getLabUserData(UserQueryHelper.ciamCudUserQuery()); } public static LabResponse getMSAUser() { - return labService.getLabUserData(UserQuery.msaUserQuery()); + return labService.getLabUserData(UserQueryHelper.msaUserQuery()); } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java similarity index 75% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java index 33d549a6..63a22601 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/LabUserNotFoundException.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java @@ -1,16 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; /** * Exception thrown when a lab user matching the query cannot be found. */ class LabUserNotFoundException extends RuntimeException { - private final UserQuery query; + private final UserQueryHelper query; - LabUserNotFoundException(UserQuery query, String message) { + LabUserNotFoundException(UserQueryHelper query, String message) { super(message); this.query = query; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java similarity index 76% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java index 482ab118..d6f6be57 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/UserQuery.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.microsoft.aad.msal4j.labapi2; +package com.microsoft.aad.msal4j.labapi; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.B2CIdentityProvider; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.FederationProvider; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.SignInAudience; -import com.microsoft.aad.msal4j.labapi2.LabServiceParameters.UserType; +import com.microsoft.aad.msal4j.labapi.LabServiceParameters.B2CIdentityProvider; +import com.microsoft.aad.msal4j.labapi.LabServiceParameters.FederationProvider; +import com.microsoft.aad.msal4j.labapi.LabServiceParameters.SignInAudience; +import com.microsoft.aad.msal4j.labapi.LabServiceParameters.UserType; -class UserQuery { +class UserQueryHelper { private UserType userType; private B2CIdentityProvider b2cIdentityProvider; private FederationProvider federationProvider; @@ -55,8 +55,8 @@ void setSignInAudience(SignInAudience signInAudience) { this.signInAudience = signInAudience; } - static UserQuery arlingtonUserQuery() { - UserQuery query = new UserQuery(); + static UserQueryHelper arlingtonUserQuery() { + UserQueryHelper query = new UserQueryHelper(); query.setUserType(UserType.CLOUD); query.setAzureEnvironment(AzureEnvironment.AZURE_US_GOVERNMENT); return query; @@ -65,8 +65,8 @@ static UserQuery arlingtonUserQuery() { /** * Gets a B2C local account (username/password) from the lab. */ - static UserQuery b2cLocalAccountQuery() { - UserQuery query = new UserQuery(); + static UserQueryHelper b2cLocalAccountQuery() { + UserQueryHelper query = new UserQueryHelper(); query.setUserType(UserType.B2C); query.setB2cIdentityProvider(B2CIdentityProvider.LOCAL); return query; @@ -76,8 +76,8 @@ static UserQuery b2cLocalAccountQuery() { * Gets a CIAM user with Custom User Domain (CUD). * Example: login.customdomain.com instead of tenant.ciamlogin.com */ - static UserQuery ciamCudUserQuery() { - UserQuery query = new UserQuery(); + static UserQueryHelper ciamCudUserQuery() { + UserQueryHelper query = new UserQueryHelper(); query.setFederationProvider(FederationProvider.CIAMCUD); query.setSignInAudience(SignInAudience.AzureAdMyOrg); return query; @@ -87,19 +87,20 @@ static UserQuery ciamCudUserQuery() { * Gets a regular Microsoft Account (MSA) user, not tied to B2C. * This is for consumer accounts like outlook.com, hotmail.com, etc. */ - static UserQuery msaUserQuery() { - UserQuery query = new UserQuery(); + static UserQueryHelper msaUserQuery() { + UserQueryHelper query = new UserQueryHelper(); query.setUserType(UserType.MSA); return query; } @Override public int hashCode() { - // Implement proper hashCode for caching int result = 17; result = 31 * result + (userType != null ? userType.hashCode() : 0); result = 31 * result + (azureEnvironment != null ? azureEnvironment.hashCode() : 0); result = 31 * result + (b2cIdentityProvider != null ? b2cIdentityProvider.hashCode() : 0); + result = 31 * result + (federationProvider != null ? federationProvider.hashCode() : 0); + result = 31 * result + (signInAudience != null ? signInAudience.hashCode() : 0); return result; } @@ -108,10 +109,12 @@ public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; - UserQuery other = (UserQuery) obj; + UserQueryHelper other = (UserQueryHelper) obj; return java.util.Objects.equals(userType, other.userType) && java.util.Objects.equals(azureEnvironment, other.azureEnvironment) && - java.util.Objects.equals(b2cIdentityProvider, other.b2cIdentityProvider); + java.util.Objects.equals(b2cIdentityProvider, other.b2cIdentityProvider) && + java.util.Objects.equals(federationProvider, other.federationProvider) && + java.util.Objects.equals(signInAudience, other.signInAudience); } //Formats the fields as they would be seen in the query parameters section of a URL diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java deleted file mode 100644 index 8ba3b426..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi2/Lab.java +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi2; - -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class Lab implements JsonSerializable { - private String labName; - private String domain; - private String tenantId; - private String federationProvider; - private String azureEnvironment; - private String authority; - - static Lab fromJson(JsonReader jsonReader) throws IOException { - Lab lab = new Lab(); - - return jsonReader.readObject(reader -> { - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - - switch (fieldName) { - case "labName": - lab.labName = reader.getString(); - break; - case "domain": - lab.domain = reader.getString(); - break; - case "tenantId": - lab.tenantId = reader.getString(); - break; - case "federationProvider": - lab.federationProvider = reader.getString(); - break; - case "azureEnvironment": - lab.azureEnvironment = reader.getString(); - break; - case "authority": - lab.authority = reader.getString(); - break; - default: - reader.skipChildren(); - break; - } - } - return lab; - }); - } - - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - - jsonWriter.writeStringField("labName", labName); - jsonWriter.writeStringField("domain", domain); - jsonWriter.writeStringField("tenantId", tenantId); - jsonWriter.writeStringField("federationProvider", federationProvider); - jsonWriter.writeStringField("azureEnvironment", azureEnvironment); - jsonWriter.writeStringField("authority", authority); - - jsonWriter.writeEndObject(); - - return jsonWriter; - } - - public String getTenantId() { - return this.tenantId; - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index 752bdd62..1d384b4f 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -4,7 +4,7 @@ package infrastructure; import com.microsoft.aad.msal4j.TestConstants; -import com.microsoft.aad.msal4j.labapi2.LabUser; +import com.microsoft.aad.msal4j.labapi.LabUser; import infrastructure.pageobjects.ADFSLoginPage; import infrastructure.pageobjects.AzureADLoginPage; import infrastructure.pageobjects.B2CLocalLoginPage; diff --git a/msal4j-sdk/src/integrationtest/java/labapi/App.java b/msal4j-sdk/src/integrationtest/java/labapi/App.java deleted file mode 100644 index eb918303..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/App.java +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class App implements JsonSerializable { - - private String appType; - private String appName; - private String appId; - private String redirectUri; - private String authority; - private String labName; - private String clientSecret; - - static App fromJson(JsonReader jsonReader) throws IOException { - App app = new App(); - - return jsonReader.readObject(reader -> { - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - - switch (fieldName) { - case "appType": - app.appType = reader.getString(); - break; - case "appName": - app.appName = reader.getString(); - break; - case "appId": - app.appId = reader.getString(); - break; - case "redirectUri": - app.redirectUri = reader.getString(); - break; - case "authority": - app.authority = reader.getString(); - break; - case "labName": - app.labName = reader.getString(); - break; - case "clientSecret": - app.clientSecret = reader.getString(); - break; - default: - reader.skipChildren(); - break; - } - } - return app; - }); - } - - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - - jsonWriter.writeStringField("appType", appType); - jsonWriter.writeStringField("appName", appName); - jsonWriter.writeStringField("appId", appId); - jsonWriter.writeStringField("redirectUri", redirectUri); - jsonWriter.writeStringField("authority", authority); - jsonWriter.writeStringField("labName", labName); - jsonWriter.writeStringField("clientSecret", clientSecret); - - jsonWriter.writeEndObject(); - - return jsonWriter; - } - - public String getAuthority() { - return authority; - } - - public String getClientSecret() { - return clientSecret; - } - -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/AppCredentialProvider.java b/msal4j-sdk/src/integrationtest/java/labapi/AppCredentialProvider.java deleted file mode 100644 index a0bdf593..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/AppCredentialProvider.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -public class AppCredentialProvider { - private KeyVaultSecretsProvider keyVaultSecretsProvider; - - private String labVaultClientId; - - private String clientId; - - private String oboClientId; - private String oboAppIdURI; - private String oboPassword; - - public AppCredentialProvider(String azureEnvironment) { - keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - - labVaultClientId = keyVaultSecretsProvider.getSecret(LabConstants.APP_ID_KEY_VAULT_SECRET); - - switch (azureEnvironment) { - case AzureEnvironment.AZURE: - clientId = "c0485386-1e9a-4663-bc96-7ab30656de7f"; - - oboClientId = "f4aa5217-e87c-42b2-82af-5624dd14ee72"; - oboAppIdURI = "api://f4aa5217-e87c-42b2-82af-5624dd14ee72"; - oboPassword = keyVaultSecretsProvider.getSecret(LabConstants.OBO_APP_PASSWORD_URL); - break; - case AzureEnvironment.AZURE_US_GOVERNMENT: - clientId = LabConstants.ARLINGTON_APP_ID; - - oboClientId = LabConstants.ARLINGTON_OBO_APP_ID; - oboAppIdURI = "https://arlmsidlab1.us/IDLABS_APP_Confidential_Client"; - - oboPassword = keyVaultSecretsProvider.getSecret(LabService.getApp(oboClientId).getClientSecret()); - break; - case AzureEnvironment.CIAM: - oboPassword = keyVaultSecretsProvider.getSecret(LabConstants.CIAM_KEY_VAULT_SECRET_KEY); - - break; - default: - throw new UnsupportedOperationException("Azure Environment - " + azureEnvironment + " unsupported"); - } - } - - public String getAppId() { - return clientId; - } - - public String getOboAppId() { - return oboClientId; - } - - public String getOboAppIdURI() { - return oboAppIdURI; - } - - public String getOboAppPassword() { - return oboPassword; - } - - public String getLabVaultAppId() { - return labVaultClientId; - } -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/AzureEnvironment.java b/msal4j-sdk/src/integrationtest/java/labapi/AzureEnvironment.java deleted file mode 100644 index b42ed52e..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/AzureEnvironment.java +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -public class AzureEnvironment { - - public static final String AZURE_CHINA = "azurechinacloud"; - public static final String AZURE = "azurecloud"; - public static final String AZURE_US_GOVERNMENT = "azureusgovernment"; - public static final String CIAM = "ciam"; -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/B2CProvider.java b/msal4j-sdk/src/integrationtest/java/labapi/B2CProvider.java deleted file mode 100644 index 03dc1983..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/B2CProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -public class B2CProvider { - public static final String FACEBOOK = "facebook"; - public static final String GOOGLE = "google"; - public static final String LOCAL = "local"; -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/FederationProvider.java b/msal4j-sdk/src/integrationtest/java/labapi/FederationProvider.java deleted file mode 100644 index f969040e..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/FederationProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -public class FederationProvider { - - public static final String NONE = "none"; - public static final String ADFS_4 = "adfsv4"; - public static final String ADFS_2019 = "adfsv2019"; - public static final String CIAMCUD = "ciamcud"; - -} - diff --git a/msal4j-sdk/src/integrationtest/java/labapi/HttpClientHelper.java b/msal4j-sdk/src/integrationtest/java/labapi/HttpClientHelper.java deleted file mode 100644 index a7c169c9..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/HttpClientHelper.java +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -import javax.net.ssl.HttpsURLConnection; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; -import java.util.Map; - -class HttpClientHelper { - - static String sendRequestToLab(String url, Map queryMap, String accessToken) throws - IOException { - return sendRequestToLab(buildUrl(url, queryMap), accessToken); - } - - static String sendRequestToLab(String url, String id, String accessToken) throws - IOException { - return sendRequestToLab(new URL(url + "/" + id), accessToken); - } - - static String sendRequestToLab(URL labUrl, String accessToken) throws - IOException { - HttpsURLConnection conn = (HttpsURLConnection) labUrl.openConnection(); - - conn.setRequestProperty("Authorization", "Bearer " + accessToken); - - conn.setReadTimeout(20000); - conn.setConnectTimeout(20000); - - StringBuilder content; - try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { - String inputLine; - content = new StringBuilder(); - while ((inputLine = in.readLine()) != null) { - content.append(inputLine); - } - } - conn.disconnect(); - return content.toString(); - } - - private static URL buildUrl(String url, Map queryMap) throws - MalformedURLException, UnsupportedOperationException { - String queryParameters; - queryParameters = queryMap.entrySet().stream() - .map(p -> encodeUTF8(p.getKey()) + "=" + encodeUTF8(p.getValue())) - .reduce((p1, p2) -> p1 + "&" + p2) - .orElse(""); - - String urlString = url + "?" + queryParameters; - return new URL(urlString); - } - - private static String encodeUTF8(String s) { - try { - return URLEncoder.encode(s, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("Error: cannot encode query parameter " + s); - } - } -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/KeyVaultSecretsProvider.java b/msal4j-sdk/src/integrationtest/java/labapi/KeyVaultSecretsProvider.java deleted file mode 100644 index 2ac007da..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/KeyVaultSecretsProvider.java +++ /dev/null @@ -1,110 +0,0 @@ -package labapi; - -import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenCredential; -import com.azure.security.keyvault.secrets.SecretClient; -import com.azure.security.keyvault.secrets.SecretClientBuilder; -import com.azure.security.keyvault.secrets.models.KeyVaultSecretIdentifier; -import com.microsoft.aad.msal4j.*; -import reactor.core.publisher.Mono; - -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class KeyVaultSecretsProvider { - - private final SecretClient secretClient; - - private static final String CLIENT_ID = TestConstants.MSIDLAB_CLIENT_ID; - public static String CERTIFICATE_ALIAS = "LabAuth.MSIDLab.com"; - - private static final String WIN_KEYSTORE = "Windows-MY"; - private static final String KEYSTORE_PROVIDER = "SunMSCAPI"; - - private static final String MAC_KEYSTORE = "KeychainStore"; - - static Map cache = new ConcurrentHashMap<>(); - - KeyVaultSecretsProvider(){ - secretClient = getAuthenticatedSecretClient(); - } - - String getSecret(String secretUrl) { - - // extract keyName from secretUrl - KeyVaultSecretIdentifier keyVaultSecretIdentifier = new KeyVaultSecretIdentifier(secretUrl); - String key = keyVaultSecretIdentifier.getName(); - - if (cache.containsKey(key)) { - return cache.get(key); - } - - String secret = secretClient.getSecret(key).getValue(); - cache.put(key, secret); - - return secret; - } - - private SecretClient getAuthenticatedSecretClient(){ - - SecretClient client = new SecretClientBuilder() - .credential(getTokenCredential()) - .vaultUrl(TestConstants.MSIDLAB_VAULT_URL) - .buildClient(); - - return client; - } - - private AccessToken requestAccessTokenForAutomation() { - IAuthenticationResult result; - try { - ConfidentialClientApplication cca = ConfidentialClientApplication.builder( - CLIENT_ID, getClientCredentialFromKeyStore()). - authority(TestConstants.MICROSOFT_AUTHORITY).sendX5c(true). - build(); - result = cca.acquireToken(ClientCredentialParameters - .builder(Collections.singleton(TestConstants.KEYVAULT_DEFAULT_SCOPE)) - .build()). - get(); - } catch (Exception e) { - throw new RuntimeException("Error acquiring token from Azure AD: " + e.getMessage()); - } - if (result != null) { - return new AccessToken(result.accessToken(), OffsetDateTime.ofInstant(result.expiresOnDate().toInstant(), ZoneOffset.UTC)); - } else { - throw new NullPointerException("Authentication result is null"); - } - } - - IClientCredential getClientCredentialFromKeyStore() { - PrivateKey key; - X509Certificate publicCertificate; - try { - String os = System.getProperty("os.name"); - KeyStore keystore; - if (os.toLowerCase().contains("windows")) { - keystore = KeyStore.getInstance(WIN_KEYSTORE, KEYSTORE_PROVIDER); - } else { - keystore = KeyStore.getInstance(MAC_KEYSTORE); - } - - keystore.load(null, null); - key = (PrivateKey) keystore.getKey(CERTIFICATE_ALIAS, null); - publicCertificate = (X509Certificate) keystore.getCertificate( - CERTIFICATE_ALIAS); - } catch (Exception e) { - throw new RuntimeException("Error getting certificate from keystore: " + e.getMessage()); - } - return ClientCredentialFactory.createFromCertificate(key, publicCertificate); - } - - private TokenCredential getTokenCredential() { - return tokenRequestContext -> Mono.defer(() -> Mono.just(requestAccessTokenForAutomation())); - } -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/LabConstants.java b/msal4j-sdk/src/integrationtest/java/labapi/LabConstants.java deleted file mode 100644 index fb43b700..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/LabConstants.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -public class LabConstants { - public static final String LAB_USER_ENDPOINT = "https://msidlab.com/api/user"; - public static final String LAB_USER_SECRET_ENDPOINT = "https://msidlab.com/api/LabSecret"; - public static final String LAB_APP_ENDPOINT = "https://msidlab.com/api/App"; - public static final String LAB_LAB_ENDPOINT = "https://msidlab.com/api/Lab"; - - public static final String APP_ID_KEY_VAULT_SECRET = "https://msidlabs.vault.azure.net/secrets/LabVaultAppID"; - public static final String APP_PASSWORD_KEY_VAULT_SECRET = "https://msidlabs.vault.azure.net/secrets/LabVaultAppSecret"; - public static final String USER_MSA_USERNAME_URL = "https://msidlabs.vault.azure.net/secrets/MSA-MSIDLAB4-UserName"; - public static final String USER_MSA_PASSWORD_URL = "https://msidlabs.vault.azure.net/secrets/MSA-MSIDLAB4-Password"; - public static final String OBO_APP_PASSWORD_URL = "https://msidlabs.vault.azure.net/secrets/TodoListServiceV2-OBO"; - public static final String CIAM_KEY_VAULT_SECRET_KEY = "https://msidlabs.vault.azure.net/secrets/MSIDLABCIAM6-cc"; - - public static final String ARLINGTON_APP_ID = "cb7faed4-b8c0-49ee-b421-f5ed16894c83"; - public static final String ARLINGTON_OBO_APP_ID = "c0555d2d-02f2-4838-802e-3463422e571d"; - - public static final String MSA_APP_ID = "9668f2bd-6103-4292-9024-84fa2d1b6fb2"; - - public static final String ARLINGTON_LAB_NAME = "ARLMSIDLAB1"; - public static final String GUEST_USER_TYPE = "Guest"; - -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/LabService.java b/msal4j-sdk/src/integrationtest/java/labapi/LabService.java deleted file mode 100644 index 70f6c9d8..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/LabService.java +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -import com.azure.json.*; -import com.microsoft.aad.msal4j.*; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -public class LabService { - - static ConfidentialClientApplication labApp; - - static void initLabApp() throws MalformedURLException { - KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - - String appID = keyVaultSecretsProvider.getSecret(LabConstants.APP_ID_KEY_VAULT_SECRET); - - labApp = ConfidentialClientApplication.builder( - appID, keyVaultSecretsProvider.getClientCredentialFromKeyStore()). - authority(TestConstants.MICROSOFT_AUTHORITY). - build(); - } - - static String getLabAccessToken() throws MalformedURLException, ExecutionException, InterruptedException { - if (labApp == null) { - initLabApp(); - } - return labApp.acquireToken(ClientCredentialParameters - .builder(Collections.singleton(TestConstants.MSIDLAB_DEFAULT_SCOPE)) - .build()). - get().accessToken(); - } - - User getUser(UserQueryParameters query) { - try { - Map queryMap = query.parameters; - String result = HttpClientHelper.sendRequestToLab( - LabConstants.LAB_USER_ENDPOINT, queryMap, getLabAccessToken()); - - User[] users = parseUserArray(result); - User user = users[0]; - if (user.getUserType().equals("Guest")) { - String secretId = user.getHomeDomain().split("\\.")[0]; - user.setPassword(getSecret(secretId)); - } else { - user.setPassword(getSecret(user.getLabName())); - } - user.setFederationProvider(query.parameters.getOrDefault(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.NONE)); - return user; - } catch (Exception ex) { - throw new RuntimeException("Error getting user from lab: " + ex.getMessage()); - } - } - - public static App getApp(String appId) { - try { - String result = HttpClientHelper.sendRequestToLab( - LabConstants.LAB_APP_ENDPOINT, appId, getLabAccessToken()); - App[] apps = parseAppArray(result); - return apps[0]; - } catch (Exception ex) { - throw new RuntimeException("Error getting app from lab: " + ex.getMessage()); - } - } - - public static Lab getLab(String labId) { - String result; - try { - result = HttpClientHelper.sendRequestToLab( - LabConstants.LAB_LAB_ENDPOINT, labId, getLabAccessToken()); - Lab[] labs = parseLabArray(result); - return labs[0]; - } catch (Exception ex) { - throw new RuntimeException("Error getting lab from lab: " + ex.getMessage()); - } - } - - public static String getSecret(String labName) { - String result; - try { - Map queryMap = new HashMap<>(); - queryMap.put("secret", labName); - result = HttpClientHelper.sendRequestToLab( - LabConstants.LAB_USER_SECRET_ENDPOINT, queryMap, getLabAccessToken()); - - UserSecret userSecret = parseUserSecret(result); - return userSecret.value; - } catch (Exception ex) { - throw new RuntimeException("Error getting user secret from lab: " + ex.getMessage()); - } - } - - // Helper methods for parsing JSON responses into specific objects - private static User[] parseUserArray(String json) { - try (JsonReader reader = JsonProviders.createReader(json)) { - reader.nextToken(); - return reader.readArray(User::fromJson).toArray(new User[0]); - } catch (IOException e) { - throw new RuntimeException("Error parsing User array: " + e.getMessage(), e); - } - } - - private static App[] parseAppArray(String json) { - try (JsonReader reader = JsonProviders.createReader(json)) { - reader.nextToken(); - return reader.readArray(App::fromJson).toArray(new App[0]); - } catch (IOException e) { - throw new RuntimeException("Error parsing App array: " + e.getMessage(), e); - } - } - - private static Lab[] parseLabArray(String json) { - try (JsonReader reader = JsonProviders.createReader(json)) { - reader.nextToken(); - return reader.readArray(Lab::fromJson).toArray(new Lab[0]); - } catch (IOException e) { - throw new RuntimeException("Error parsing Lab array: " + e.getMessage(), e); - } - } - - private static UserSecret parseUserSecret(String json) { - try (JsonReader reader = JsonProviders.createReader(json)) { - return UserSecret.fromJson(reader); - } catch (IOException e) { - throw new RuntimeException("Error parsing UserSecret: " + e.getMessage(), e); - } - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/LabUserProvider.java b/msal4j-sdk/src/integrationtest/java/labapi/LabUserProvider.java deleted file mode 100644 index d7b13f03..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/LabUserProvider.java +++ /dev/null @@ -1,118 +0,0 @@ -//---------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -//------------------------------------------------------------------------------ - -package labapi; - -import java.util.HashMap; -import java.util.Map; - -public class LabUserProvider { - - private static LabUserProvider instance; - - private final LabService labService; - private Map userCache; - - private LabUserProvider() { - labService = new LabService(); - userCache = new HashMap<>(); - } - - public static synchronized LabUserProvider getInstance() { - if (instance == null) { - instance = new LabUserProvider(); - } - return instance; - } - - public User getDefaultUser() { - return getDefaultUser(AzureEnvironment.AZURE); - } - - public User getDefaultUser(String azureEnvironment) { - - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, azureEnvironment); - - return getLabUser(query); - } - - public User getFederatedAdfsUser(String federationProvider) { - return getFederatedAdfsUser(AzureEnvironment.AZURE, federationProvider); - } - - public User getFederatedAdfsUser(String azureEnvironment, String federationProvider) { - - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, azureEnvironment); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, federationProvider); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.FEDERATED); - - return getLabUser(query); - } - - public User getOnPremAdfsUser(String federationProvider) { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, federationProvider); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.ON_PREM); - - return getLabUser(query); - } - - public User getB2cUser(String azureEnvironment, String b2cProvider) { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, azureEnvironment); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.B2C); - query.parameters.put(UserQueryParameters.B2C_PROVIDER, b2cProvider); - - return getLabUser(query); - } - - public User getMSAUser() { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.USER_TYPE, UserType.MSA); - - return getLabUser(query); - } - - public User getUserByAzureEnvironment(String azureEnvironment) { - - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, azureEnvironment); - - return getLabUser(query); - } - - public User getUserByGuestHomeAzureEnvironments(String guestEnvironment, String homeEnvironment) { - - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.USER_TYPE, "guest"); - query.parameters.put(UserQueryParameters.AZURE_ENVIRONMENT, guestEnvironment); - query.parameters.put(UserQueryParameters.HOME_AZURE_ENVIRONMENT, homeEnvironment); - query.parameters.put(UserQueryParameters.GUEST_HOME_DIN, "hostazuread"); - query.parameters.put(UserQueryParameters.SIGN_IN_AUDIENCE, "azureadmyorg"); - - return getLabUser(query); - } - - public User getCiamCudUser() { - UserQueryParameters query = new UserQueryParameters(); - query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.CIAMCUD); - query.parameters.put(UserQueryParameters.SIGN_IN_AUDIENCE, "azureadmyorg"); - - return getLabUser(query); - } - - public User getLabUser(UserQueryParameters userQuery) { - if (userCache.containsKey(userQuery)) { - return userCache.get(userQuery); - } - User response = labService.getUser(userQuery); - userCache.put(userQuery, response); - return response; - } -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/User.java b/msal4j-sdk/src/integrationtest/java/labapi/User.java deleted file mode 100644 index 9c189e0f..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/User.java +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class User implements JsonSerializable { - private String appId; - private String objectId; - private String userType; - private String displayName; - private String licenses; - private String upn; - private String mfa; - private String protectionPolicy; - private String homeDomain; - private String homeUPN; - private String b2cProvider; - private String labName; - private String lastUpdatedBy; - private String lastUpdatedDate; - private String tenantID; - private String password; - private String federationProvider; - - static User fromJson(JsonReader jsonReader) throws IOException { - User user = new User(); - - return jsonReader.readObject(reader -> { - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - - switch (fieldName) { - case "appId": - user.appId = reader.getString(); - break; - case "objectId": - user.objectId = reader.getString(); - break; - case "userType": - user.userType = reader.getString(); - break; - case "displayName": - user.displayName = reader.getString(); - break; - case "licenses": - user.licenses = reader.getString(); - break; - case "upn": - user.upn = reader.getString(); - break; - case "mfa": - user.mfa = reader.getString(); - break; - case "protectionPolicy": - user.protectionPolicy = reader.getString(); - break; - case "homeDomain": - user.homeDomain = reader.getString(); - break; - case "homeUPN": - user.homeUPN = reader.getString(); - break; - case "b2cProvider": - user.b2cProvider = reader.getString(); - break; - case "labName": - user.labName = reader.getString(); - break; - case "lastUpdatedBy": - user.lastUpdatedBy = reader.getString(); - break; - case "lastUpdatedDate": - user.lastUpdatedDate = reader.getString(); - break; - case "tenantID": - user.tenantID = reader.getString(); - break; - default: - reader.skipChildren(); - break; - } - } - return user; - }); - } - - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - - jsonWriter.writeStringField("appId", appId); - jsonWriter.writeStringField("objectId", objectId); - jsonWriter.writeStringField("userType", userType); - jsonWriter.writeStringField("displayName", displayName); - jsonWriter.writeStringField("licenses", licenses); - jsonWriter.writeStringField("upn", upn); - jsonWriter.writeStringField("mfa", mfa); - jsonWriter.writeStringField("protectionPolicy", protectionPolicy); - jsonWriter.writeStringField("homeDomain", homeDomain); - jsonWriter.writeStringField("homeUPN", homeUPN); - jsonWriter.writeStringField("b2cProvider", b2cProvider); - jsonWriter.writeStringField("labName", labName); - jsonWriter.writeStringField("lastUpdatedBy", lastUpdatedBy); - jsonWriter.writeStringField("lastUpdatedDate", lastUpdatedDate); - jsonWriter.writeStringField("tenantID", tenantID); - - jsonWriter.writeEndObject(); - - return jsonWriter; - } - - public String getAppId() { - return this.appId; - } - - public String getUserType() { - return this.userType; - } - - public String getUpn() { - return this.upn; - } - - public String getHomeDomain() { - return this.homeDomain; - } - - public String getHomeUPN() { - return this.homeUPN; - } - - public String getB2cProvider() { - return this.b2cProvider; - } - - public String getLabName() { - return this.labName; - } - - public String getTenantID() { - return this.tenantID; - } - - public String getPassword() { - return this.password; - } - - public String getFederationProvider() { - return this.federationProvider; - } - - public void setUpn(String upn) { - this.upn = upn; - } - - public void setPassword(String password) { - this.password = password; - } - - public void setFederationProvider(String federationProvider) { - this.federationProvider = federationProvider; - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/UserQueryParameters.java b/msal4j-sdk/src/integrationtest/java/labapi/UserQueryParameters.java deleted file mode 100644 index fb638c78..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/UserQueryParameters.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -import java.util.HashMap; -import java.util.Map; - -public class UserQueryParameters { - - public static final String USER_TYPE = "usertype"; - public static final String B2C_PROVIDER = "b2cprovider"; - public static final String FEDERATION_PROVIDER = "federationprovider"; - public static final String AZURE_ENVIRONMENT = "azureenvironment"; - public static final String HOME_AZURE_ENVIRONMENT = "guesthomeazureenvironment"; - public static final String GUEST_HOME_DIN = "guesthomedin"; - public static final String SIGN_IN_AUDIENCE = "signInAudience"; - - public Map parameters = new HashMap<>(); -} diff --git a/msal4j-sdk/src/integrationtest/java/labapi/UserSecret.java b/msal4j-sdk/src/integrationtest/java/labapi/UserSecret.java deleted file mode 100644 index 9475b65f..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/UserSecret.java +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -import com.azure.json.JsonReader; -import com.azure.json.JsonSerializable; -import com.azure.json.JsonToken; -import com.azure.json.JsonWriter; - -import java.io.IOException; - -public class UserSecret implements JsonSerializable { - - String secret; - String value; - - static UserSecret fromJson(JsonReader jsonReader) throws IOException { - UserSecret userSecret = new UserSecret(); - - return jsonReader.readObject(reader -> { - while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); - reader.nextToken(); - - switch (fieldName) { - case "secret": - userSecret.secret = reader.getString(); - break; - case "value": - userSecret.value = reader.getString(); - break; - default: - reader.skipChildren(); - break; - } - } - return userSecret; - }); - } - - @Override - public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { - jsonWriter.writeStartObject(); - - jsonWriter.writeStringField("secret", secret); - jsonWriter.writeStringField("value", value); - - jsonWriter.writeEndObject(); - - return jsonWriter; - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/UserType.java b/msal4j-sdk/src/integrationtest/java/labapi/UserType.java deleted file mode 100644 index 31a3cb58..00000000 --- a/msal4j-sdk/src/integrationtest/java/labapi/UserType.java +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package labapi; - -public class UserType { - public static final String FEDERATED = "federated"; - public static final String ON_PREM = "onprem"; - public static final String GUEST = "guest"; - public static final String MSA = "msa"; - public static final String B2C = "b2c"; -} From a8a8073232bd5ec3fc50c6510bf35f41073fd72d Mon Sep 17 00:00:00 2001 From: avdunn Date: Mon, 24 Nov 2025 15:09:23 -0800 Subject: [PATCH 12/14] New dependencies are only for tests --- msal4j-sdk/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index 35868b9f..b426cbbe 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -133,11 +133,13 @@ com.azure azure-security-keyvault-certificates 4.5.0 + test com.azure azure-identity 1.10.0 + test From 8648fa23408c44ed00486d645be0eb5217ca3251 Mon Sep 17 00:00:00 2001 From: avdunn Date: Wed, 3 Dec 2025 09:33:18 -0800 Subject: [PATCH 13/14] Remove ID Lab API code, rename classes/methods for better clarity, add more comments --- .../AcquireTokenInteractiveIT.java | 53 ++-- .../AcquireTokenSilentIT.java | 44 +-- .../AuthorizationCodeIT.java | 26 +- .../AzureEnvironmentIT.java | 6 +- .../DeviceCodeIT.java | 68 ++--- .../HttpClientIT.java | 16 +- .../OnBehalfOfIT.java | 4 +- .../RefreshTokenIT.java | 4 +- .../SeleniumTest.java | 4 +- .../TokenCacheIT.java | 8 +- .../UsernamePasswordIT.java | 22 +- .../labapi/{LabApp.java => AppConfig.java} | 9 +- .../aad/msal4j/labapi/AzureEnvironment.java | 8 - .../aad/msal4j/labapi/KeyVaultRegistry.java | 9 + .../labapi/KeyVaultSecretsProvider.java | 28 +- .../aad/msal4j/labapi/LabApiService.java | 289 ------------------ .../labapi/{Lab.java => LabConfig.java} | 23 +- .../aad/msal4j/labapi/LabConfigHelper.java | 56 ++++ .../aad/msal4j/labapi/LabConstants.java | 29 -- .../aad/msal4j/labapi/LabHttpHelper.java | 89 ------ .../aad/msal4j/labapi/LabResponse.java | 40 +-- .../msal4j/labapi/LabServiceParameters.java | 34 --- .../aad/msal4j/labapi/LabUserHelper.java | 42 --- .../labapi/LabUserNotFoundException.java | 23 -- .../labapi/{LabUser.java => UserConfig.java} | 6 +- .../aad/msal4j/labapi/UserQueryHelper.java | 152 --------- .../infrastructure/SeleniumExtensions.java | 10 +- 27 files changed, 254 insertions(+), 848 deletions(-) rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/{LabApp.java => AppConfig.java} (91%) delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/{Lab.java => LabConfig.java} (74%) create mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfigHelper.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java rename msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/{LabUser.java => UserConfig.java} (96%) delete mode 100644 msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java index 483a1fef..69622bff 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenInteractiveIT.java @@ -38,16 +38,16 @@ public void startBrowser() { @Test void acquireTokenInteractive_ManagedUser() { - LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantAppPublicClient(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getMultiTenantAppPublicClientConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", TestConstants.GRAPH_DEFAULT_SCOPE); } @Test void acquireTokenInteractive_Arlington() { - LabResponse labResponse = LabUserHelper.getArlingtonUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getArlingtonConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", TestConstants.GRAPH_DEFAULT_SCOPE); } @@ -55,38 +55,39 @@ void acquireTokenInteractive_Arlington() { @Test() @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void acquireTokenInteractive_ADFSv2022() { - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(); + LabResponse labResponse = LabConfigHelper.getAdfsConfig(); - LabUser user = labResponse.getUser(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority() + "organizations/", TestConstants.ADFS_SCOPE); } @Test void acquireTokenWithAuthorizationCode_B2C_Local() { - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); - LabUser user = labResponse.getUser(); - assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY); + LabResponse labResponse = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); + assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY, labResponse.getApp().getAppId()); } @Test void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); - LabUser user = labResponse.getUser(); - assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT); + LabResponse labResponse = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); + assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT, labResponse.getApp().getAppId()); } @Test void acquireTokenInteractive_ManagedUser_InstanceAware() { - LabResponse labResponse = LabUserHelper.getArlingtonUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getArlingtonConfig(); + UserConfig user = labResponse.getUser(); + assertAcquireTokenInstanceAware(user, labResponse.getApp().getAppId(), labResponse.getLab().getTenantId()); } @Test void acquireTokenInteractive_Ciam() { - LabResponse labResponse = LabUserHelper.getCiamCudUser(); - LabUser user = labResponse.getUser(); - LabApp app = labResponse.getApp(); + LabResponse labResponse = LabConfigHelper.getCiamConfig(); + UserConfig user = labResponse.getUser(); + AppConfig app = labResponse.getApp(); Map extraQueryParameters = new HashMap<>(); @@ -128,7 +129,7 @@ void acquireTokenInteractive_Ciam() { assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenCommon(LabUser user, String appId, String authority, String scope) { + private void assertAcquireTokenCommon(UserConfig user, String appId, String authority, String scope) { PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, authority); IAuthenticationResult result = acquireTokenInteractive( @@ -140,23 +141,23 @@ private void assertAcquireTokenCommon(LabUser user, String appId, String authori assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenB2C(LabUser user, String authority) { + private void assertAcquireTokenB2C(UserConfig user, String authority, String appId) { PublicClientApplication pca; try { pca = PublicClientApplication.builder( - user.getAppId()). + appId). b2cAuthority(authority + TestConstants.B2C_SIGN_IN_POLICY). build(); } catch (MalformedURLException ex) { throw new RuntimeException(ex.getMessage()); } - IAuthenticationResult result = acquireTokenInteractive(user, pca, user.getAppId()); + IAuthenticationResult result = acquireTokenInteractive(user, pca, appId); IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - private void assertAcquireTokenInstanceAware(LabUser user, String appId, String tenantId) { + private void assertAcquireTokenInstanceAware(UserConfig user, String appId, String tenantId) { PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, TestConstants.MICROSOFT_AUTHORITY_HOST + tenantId); IAuthenticationResult result = acquireTokenInteractive_instanceAware(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); @@ -188,7 +189,7 @@ private IAuthenticationResult acquireTokenSilently(IPublicClientApplication pca, } private IAuthenticationResult acquireTokenInteractive( - LabUser user, + UserConfig user, PublicClientApplication pca, String scope) { @@ -218,7 +219,7 @@ private IAuthenticationResult acquireTokenInteractive( } private IAuthenticationResult acquireTokenInteractive_instanceAware( - LabUser user, + UserConfig user, PublicClientApplication pca, String scope) { @@ -249,10 +250,10 @@ private IAuthenticationResult acquireTokenInteractive_instanceAware( class SeleniumOpenBrowserAction implements OpenBrowserAction { - private LabUser user; + private UserConfig user; private PublicClientApplication pca; - SeleniumOpenBrowserAction(LabUser user, PublicClientApplication pca) { + SeleniumOpenBrowserAction(UserConfig user, PublicClientApplication pca) { this.user = user; this.pca = pca; } diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java index 99e4c689..faf908c3 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AcquireTokenSilentIT.java @@ -34,8 +34,8 @@ void acquireTokenSilent_OrganizationAuthority_TokenRefreshed() throws Exception @Test void acquireTokenSilent_LabAuthority_TokenNotRefreshed() throws Exception { // Access token should be returned from cache, and not using refresh token - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -57,8 +57,8 @@ void acquireTokenSilent_LabAuthority_TokenNotRefreshed() throws Exception { @Test void acquireTokenSilent_ForceRefresh() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -80,16 +80,16 @@ void acquireTokenSilent_ForceRefresh() throws Exception { @Test void acquireTokenSilent_usingOrganizationsAuthority_returnCachedAt() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), TestConstants.ORGANIZATIONS_AUTHORITY, user); } @Test void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId(), user); } @@ -144,8 +144,8 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrow @Test void acquireTokenSilent_WithRefreshOn() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -189,8 +189,8 @@ void acquireTokenSilent_WithRefreshOn() throws Exception { @Test void acquireTokenSilent_TenantAsParameter() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -219,8 +219,8 @@ void acquireTokenSilent_TenantAsParameter() throws Exception { @Test void acquireTokenSilent_emptyStringScope() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -239,8 +239,8 @@ void acquireTokenSilent_emptyStringScope() throws Exception { @Test void acquireTokenSilent_emptyScopeSet() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( @@ -268,8 +268,8 @@ void acquireTokenSilent_emptyScopeSet() throws Exception { @Test public void acquireTokenSilent_ClaimsForceRefresh() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( @@ -320,7 +320,7 @@ private IConfidentialClientApplication getConfidentialClientApplications() throw build(); } - private void acquireTokenSilent_returnCachedTokens(String appId, String authority, LabUser user) throws Exception { + private void acquireTokenSilent_returnCachedTokens(String appId, String authority, UserConfig user) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( appId). @@ -343,8 +343,8 @@ private void acquireTokenSilent_returnCachedTokens(String appId, String authorit private IPublicClientApplication getPublicClientApplicationWithTokensInCache() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -363,7 +363,7 @@ private IAuthenticationResult acquireTokenSilently(IPublicClientApplication pca, .get(); } - private IAuthenticationResult acquireTokenUsernamePassword(LabUser user, IPublicClientApplication pca, String scope) throws InterruptedException, ExecutionException { + private IAuthenticationResult acquireTokenUsernamePassword(UserConfig user, IPublicClientApplication pca, String scope) throws InterruptedException, ExecutionException { Map map = new HashMap<>(); map.put("test","test"); return pca.acquireToken(UserNamePasswordParameters. diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java index 0c1faaa0..3d452845 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AuthorizationCodeIT.java @@ -39,16 +39,16 @@ public void startBrowser() { @Test public void acquireTokenWithAuthorizationCode_ManagedUser() { - LabResponse labResponse = LabUserHelper.getDefaultUserMultiTenantAppPublicClient(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getMultiTenantAppPublicClientConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), null); } @Test public void acquireTokenWithAuthorizationCode_B2C_Local() { - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenB2C(user); } @@ -56,9 +56,9 @@ public void acquireTokenWithAuthorizationCode_B2C_Local() { public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { String authorityCud = "https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/"; - LabResponse labResponse = LabUserHelper.getCiamCudUser(); - LabUser user = labResponse.getUser(); - LabApp app = labResponse.getApp(); + LabResponse labResponse = LabConfigHelper.getCiamConfig(); + UserConfig user = labResponse.getUser(); + AppConfig app = labResponse.getApp(); PublicClientApplication pca = PublicClientApplication.builder( app.getAppId()). @@ -90,13 +90,13 @@ public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { @Test @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void acquireTokenWithAuthorizationCode_ADFSv2022() { - LabResponse labResponse = LabUserHelper.getDefaultAdfsUser(); + LabResponse labResponse = LabConfigHelper.getAdfsConfig(); - LabUser user = labResponse.getUser(); + UserConfig user = labResponse.getUser(); assertAcquireTokenADFS(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority() + "organizations/"); } - private void assertAcquireTokenADFS(LabUser user, String appId, String authority) { + private void assertAcquireTokenADFS(UserConfig user, String appId, String authority) { PublicClientApplication pca; try { pca = PublicClientApplication.builder( @@ -117,7 +117,7 @@ private void assertAcquireTokenADFS(LabUser user, String appId, String authority assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenAAD(LabUser user, String appId, Map> parameters) { + private void assertAcquireTokenAAD(UserConfig user, String appId, Map> parameters) { PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, TestConstants.COMMON_AUTHORITY); @@ -131,7 +131,7 @@ private void assertAcquireTokenAAD(LabUser user, String appId, Map> parameters) { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java index 6f3e9170..940f42f7 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/AzureEnvironmentIT.java @@ -15,9 +15,9 @@ class AzureEnvironmentIT { @Test void acquireTokenWithUsernamePassword_AzureGovernment() throws Exception { - LabResponse labResponse = LabUserHelper.getArlingtonUser(); - LabUser user = labResponse.getUser(); - LabApp app = labResponse.getApp(); + LabResponse labResponse = LabConfigHelper.getArlingtonConfig(); + UserConfig user = labResponse.getUser(); + AppConfig app = labResponse.getApp(); PublicClientApplication pca = PublicClientApplication.builder( app.getAppId()). diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index eef7288d..827ca255 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -30,8 +30,8 @@ void setUp() { @Test void DeviceCodeFlowADTest() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = IntegrationTestHelper.createPublicApp(labResponse.getApp().getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId()); @@ -46,39 +46,39 @@ void DeviceCodeFlowADTest() throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - //TODO: This test is failing intermittently in the pipeline runs for the same commit, but always passes locally. Disabling until we can investigate more. + //TODO: This test is failing intermittently due to inconsistent login page layouts and is commented out until fixed. //@Test() - void DeviceCodeFlowMSATest() throws Exception { - - LabResponse labResponse = LabUserHelper.getMSAUser(); - LabUser user = labResponse.getUser(); - LabApp app = labResponse.getApp(); - - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(app.getAppId(), TestConstants.CONSUMERS_AUTHORITY); - - Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { - runAutomatedDeviceCodeFlow(deviceCode, user); - }; - - IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters - .builder(Collections.singleton(""), - deviceCodeConsumer) - .build()) - .get(); - - assertNotNull(result); - assertNotNull(result.accessToken()); - - result = pca.acquireTokenSilently(SilentParameters. - builder(Collections.singleton(""), result.account()). - build()) - .get(); - - assertNotNull(result); - assertNotNull(result.accessToken()); - } - - private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, LabUser user) { +// void DeviceCodeFlowMSATest() throws Exception { +// +// LabResponse labResponse = LabConfigHelper.getMSAUser(); +// UserConfig user = labResponse.getUser(); +// AppConfig app = labResponse.getApp(); +// +// PublicClientApplication pca = IntegrationTestHelper.createPublicApp(app.getAppId(), TestConstants.CONSUMERS_AUTHORITY); +// +// Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { +// runAutomatedDeviceCodeFlow(deviceCode, user); +// }; +// +// IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters +// .builder(Collections.singleton(""), +// deviceCodeConsumer) +// .build()) +// .get(); +// +// assertNotNull(result); +// assertNotNull(result.accessToken()); +// +// result = pca.acquireTokenSilently(SilentParameters. +// builder(Collections.singleton(""), result.account()). +// build()) +// .get(); +// +// assertNotNull(result); +// assertNotNull(result.accessToken()); +// } + + private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, UserConfig user) { SeleniumExtensions.performDeviceCodeLogin( seleniumDriver, deviceCode.verificationUri(), diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java index 71173911..f63370e4 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java @@ -20,28 +20,28 @@ class HttpClientIT { @Test void acquireToken_okHttpClient() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new OkHttpClientAdapter()); } @Test void acquireToken_apacheHttpClient() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new ApacheHttpClientAdapter()); } @Test void acquireToken_readTimeout() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); //Set a 1ms read timeout, which will almost certainly occur before the service can respond assertAcquireTokenCommon_WithTimeout(user, labResponse.getApp().getAppId(), 1); } - private void assertAcquireTokenCommon(LabUser user, String appId, IHttpClient httpClient) + private void assertAcquireTokenCommon(UserConfig user, String appId, IHttpClient httpClient) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( appId). @@ -62,7 +62,7 @@ private void assertAcquireTokenCommon(LabUser user, String appId, IHttpClient ht assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenCommon_WithTimeout(LabUser user, String appId, int readTimeout) + private void assertAcquireTokenCommon_WithTimeout(UserConfig user, String appId, int readTimeout) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( appId). diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java index 3612b4b9..bd80d986 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/OnBehalfOfIT.java @@ -122,8 +122,8 @@ private void assertResultNotNull(IAuthenticationResult result) { private String getAccessToken() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); String clientId = TestConstants.OBO_CLIENT_ID; String apiReadScope = TestConstants.OBO_APP_ID_URI + "/access_as_user"; diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java index 2e0d2e5a..67e6698e 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java @@ -17,8 +17,8 @@ class RefreshTokenIT { private PublicClientApplication pca; private void setUp() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java index 4f50b907..922c03c4 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java @@ -3,7 +3,7 @@ package com.microsoft.aad.msal4j; -import com.microsoft.aad.msal4j.labapi.LabUser; +import com.microsoft.aad.msal4j.labapi.UserConfig; import infrastructure.SeleniumExtensions; import org.openqa.selenium.WebDriver; import org.slf4j.Logger; @@ -36,7 +36,7 @@ public void startUpBrowser() { seleniumDriver = SeleniumExtensions.createDefaultWebDriver(); } - void runSeleniumAutomatedLogin(LabUser user, AbstractClientApplicationBase app) { + void runSeleniumAutomatedLogin(UserConfig user, AbstractClientApplicationBase app) { AuthorityType authorityType = app.authenticationAuthority.authorityType; try { diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index 7f7ca005..af99e1c3 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java @@ -19,8 +19,8 @@ class TokenCacheIT { @Test void singleAccountInCache_RemoveAccountTest() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -54,8 +54,8 @@ void singleAccountInCache_RemoveAccountTest() throws Exception { @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser guestUser = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig guestUser = labResponse.getUser(); String dataToInitCache = TestHelper.readResource( this.getClass(), diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java index fb466c9d..fffc8403 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/UsernamePasswordIT.java @@ -16,14 +16,14 @@ class UsernamePasswordIT { @Test void acquireTokenWithUsernamePassword_Managed() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); assertAcquireTokenCommon(labResponse.getUser(), TestConstants.ORGANIZATIONS_AUTHORITY, TestConstants.GRAPH_DEFAULT_SCOPE, labResponse.getApp().getAppId()); } @Test void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { - LabResponse labResponse = LabUserHelper.getDefaultUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon( user, @@ -36,10 +36,10 @@ void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { void acquireTokenWithUsernamePassword_Ciam() throws Exception { Map extraQueryParameters = new HashMap<>(); - LabResponse labResponse = LabUserHelper.getCiamCudUser(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getCiamConfig(); + UserConfig user = labResponse.getUser(); - PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) + PublicClientApplication pca = PublicClientApplication.builder(labResponse.getApp().getAppId()) .authority("https://" + user.getLabName() + ".ciamlogin.com/") .build(); @@ -54,7 +54,7 @@ void acquireTokenWithUsernamePassword_Ciam() throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - private void assertAcquireTokenCommon(LabUser user, String authority, String scope, String appId) + private void assertAcquireTokenCommon(UserConfig user, String authority, String scope, String appId) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( @@ -75,8 +75,8 @@ private void assertAcquireTokenCommon(LabUser user, String authority, String sco @Test void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Exception { - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). @@ -105,8 +105,8 @@ void acquireTokenWithUsernamePassword_B2C_CustomAuthority() throws Exception { @Test void acquireTokenWithUsernamePassword_B2C_LoginMicrosoftOnline() throws Exception { - LabResponse labResponse = LabUserHelper.getB2CLocalAccount(); - LabUser user = labResponse.getUser(); + LabResponse labResponse = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( labResponse.getApp().getAppId()). diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApp.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AppConfig.java similarity index 91% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApp.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AppConfig.java index 6b55c6d1..42eb3c98 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApp.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AppConfig.java @@ -10,7 +10,10 @@ import java.io.IOException; -public class LabApp implements JsonSerializable { +/** + * Represents a JSON response describing an Azure app registration. + */ +public class AppConfig implements JsonSerializable { private String appType; private String appName; @@ -20,8 +23,8 @@ public class LabApp implements JsonSerializable { private String labName; private String clientSecret; - static LabApp fromJson(JsonReader jsonReader) throws IOException { - LabApp app = new LabApp(); + static AppConfig fromJson(JsonReader jsonReader) throws IOException { + AppConfig app = new AppConfig(); return jsonReader.readObject(reader -> { while (reader.nextToken() != JsonToken.END_OBJECT) { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java deleted file mode 100644 index f4881d54..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AzureEnvironment.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -class AzureEnvironment { - static final String AZURE_US_GOVERNMENT = "azureusgovernment"; -} diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java index d1cf81ec..34b7fd37 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java @@ -3,6 +3,9 @@ package com.microsoft.aad.msal4j.labapi; +/** + * Helper class to provide access to different Key Vault secrets providers. + */ public class KeyVaultRegistry { private static final KeyVaultSecretsProvider MSID_LAB_PROVIDER = new KeyVaultSecretsProvider(KeyVaultSecretsProvider.KeyVaultInstance.MSID_LAB); @@ -10,10 +13,16 @@ public class KeyVaultRegistry { private static final KeyVaultSecretsProvider MSAL_TEAM_PROVIDER = new KeyVaultSecretsProvider(KeyVaultSecretsProvider.KeyVaultInstance.MSAL_TEAM); + /** + * This Key Vault is primarily used for frequently rotated credentials. + */ public static KeyVaultSecretsProvider getMsidLabProvider() { return MSID_LAB_PROVIDER; } + /** + * This Key Vault is primarily used for user/app/etc. configuration and other long-lived info. + */ public static KeyVaultSecretsProvider getMsalTeamProvider() { return MSAL_TEAM_PROVIDER; } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java index b80eae4c..204a662c 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java @@ -22,6 +22,12 @@ import java.time.ZoneOffset; import java.util.Collections; +/** + * Provider for retrieving secrets from Azure Key Vault. + *

+ * Supports authentication using client certificates stored in OS keystore. + * Secrets can be retrieved by name and deserialized into LabResponse objects. + */ public class KeyVaultSecretsProvider implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(KeyVaultSecretsProvider.class); @@ -33,13 +39,14 @@ public class KeyVaultSecretsProvider implements AutoCloseable { static class KeyVaultInstance { /** - * The KeyVault maintained by the MSID. It is recommended for use. + * This Key Vault is maintained by the MSID LabConfig team. It is generally used for frequently rotated credentials and + * other sensitive configuration. */ static final String MSID_LAB = "https://msidlabs.vault.azure.net"; /** - * The KeyVault maintained by the MSAL.NET team and have full control over. - * Should be used temporarily - secrets should be stored and managed by MSID Lab. + * The KeyVault maintained by the MSAL/MISE team. It is generally used for static user/app/etc. info and + * other low-risk configuration. */ static final String MSAL_TEAM = "https://id4skeyvault.vault.azure.net/"; } @@ -70,10 +77,6 @@ static class KeyVaultInstance { log.debug("KeyVault secrets provider initialized successfully"); } - public KeyVaultSecretsProvider() { - this(KeyVaultInstance.MSID_LAB); - } - /** * Get a secret by name from Key Vault. * @@ -176,8 +179,7 @@ public Object getLabData(String secretName) { if (labData == null || labData.isEmpty()) { log.error("Key Vault secret '{}' is empty", secretName); - throw new LabUserNotFoundException(new UserQueryHelper(), - "Found no content for secret '" + secretName + "' in Key Vault."); + throw new RuntimeException("Found no content for secret '" + secretName + "' in Key Vault."); } // Check if the value is JSON @@ -189,8 +191,7 @@ public Object getLabData(String secretName) { if (response == null) { log.error("Failed to deserialize Key Vault secret '{}' to LabResponse", secretName); - throw new LabUserNotFoundException(new UserQueryHelper(), - "Failed to deserialize Key Vault secret '" + secretName + "' to LabResponse."); + throw new RuntimeException("Failed to deserialize Key Vault secret '" + secretName + "' to LabResponse."); } log.debug("Retrieved LabResponse from Key Vault '{}': {}", secretName, @@ -249,8 +250,7 @@ public LabResponse mergeLabResponses(String... secretNames) { if (!hasValidResponse) { log.error("Failed to merge secrets - no valid LabResponse found: {}", String.join(", ", secretNames)); - throw new LabUserNotFoundException(new UserQueryHelper(), - "Failed to create merged LabResponse from secrets: " + + throw new RuntimeException("Failed to create merged LabResponse from secrets: " + String.join(", ", secretNames)); } @@ -267,7 +267,7 @@ public LabResponse mergeLabResponses(String... secretNames) { /** * Fetch user password from Key Vault. - * Note: This should only be called on instances configured for the MSID Lab vault. + * Note: This should only be called on instances configured for the MSID LabConfig vault. * * @param userLabName The lab name of the user (used as secret name) * @return The user's password diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java deleted file mode 100644 index 6fb8aee8..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabApiService.java +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -import com.azure.json.JsonProviders; -import com.azure.json.JsonReader; -import com.microsoft.aad.msal4j.ClientCredentialParameters; -import com.microsoft.aad.msal4j.ConfidentialClientApplication; -import com.microsoft.aad.msal4j.IAuthenticationResult; -import com.microsoft.aad.msal4j.labapi.LabServiceParameters.MFA; -import com.microsoft.aad.msal4j.labapi.LabServiceParameters.ProtectionPolicy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Wrapper for lab service API interactions. - */ -class LabApiService { - - private static final Logger log = LoggerFactory.getLogger(LabApiService.class); - - private ConfidentialClientApplication labApp; - private final ConcurrentHashMap userCache = new ConcurrentHashMap<>(); - - /** - * Get lab user data with caching support. - * - * @param query The UserQueryHelper to search for - * @return LabResponse - */ - LabResponse getLabUserData(UserQueryHelper query) { - if (userCache.containsKey(query)) { - LabResponse cached = userCache.get(query); - log.debug("Lab cache hit: {}", - cached.getUser() != null ? cached.getUser().getUpn() : "N/A"); - return cached; - } - - LabResponse response = getLabResponseFromApi(query); - - if (response == null) { - log.error("No lab user found for query"); - throw new LabUserNotFoundException(query, "Found no users for the given query."); - } - - log.info("Lab API returned user: {}", - response.getUser() != null ? response.getUser().getUpn() : "N/A"); - - userCache.put(query, response); - return response; - } - - /** - * Returns a test user account for use in testing. - * - * @param query Any and all parameters that the returned user should satisfy - * @return LabResponse with user that matches the query - */ - private LabResponse getLabResponseFromApi(UserQueryHelper query) { - log.debug("Querying lab API for user with parameters: {}", query); - - try { - String result = runQuery(query); - - if (result == null || result.trim().isEmpty()) { - log.error("No lab user found for query"); - throw new LabUserNotFoundException(query, - "No lab user with specified parameters exists"); - } - log.debug("Lab API returned {} characters of data", result.length()); - - LabResponse response = createLabResponseFromResultString(result); - - if (response != null && response.getUser() != null) { - log.info("Successfully retrieved lab user: {}", response.getUser().getUpn()); - } - - return response; - } catch (Exception e) { - log.error("Failed to get lab response: {}", e.getMessage()); - throw new RuntimeException("Failed to get lab response", e); - } - } - - /** - * Create a LabResponse object from JSON result string. - * - * @param result JSON string containing lab user array - * @return Populated LabResponse - */ - LabResponse createLabResponseFromResultString(String result) { - try { - log.debug("Parsing lab user JSON response"); - - List userResponses; - try (JsonReader jsonReader = JsonProviders.createReader(result)) { - userResponses = jsonReader.readArray(LabUser::fromJson); - } - - if (userResponses.isEmpty()) { - log.error("Lab API returned empty user array"); - throw new RuntimeException("Lab API returned empty user array"); - } - - LabUser user = userResponses.get(0); - log.debug("Parsed lab user: {}, fetching app and lab info", user.getUpn()); - - // Fetch app and lab info - String appEndpoint = LabConstants.LAB_APP_ENDPOINT + user.getAppId(); - log.debug("Fetching app info from: {}", appEndpoint); - String appResponse = getLabResponse(appEndpoint); - - List labApps; - try (JsonReader appReader = JsonProviders.createReader(appResponse)) { - labApps = appReader.readArray(LabApp::fromJson); - } - - if (labApps.isEmpty()) { - log.error("No lab app found for appId: {}", user.getAppId()); - throw new RuntimeException("No lab app found for appId: " + user.getAppId()); - } - log.debug("Successfully parsed lab app: {}", labApps.get(0).getAppId()); - - String labInfoEndpoint = LabConstants.LAB_INFO_ENDPOINT + user.getLabName(); - log.debug("Fetching lab info from: {}", labInfoEndpoint); - String labInfoResponse = getLabResponse(labInfoEndpoint); - - List labs; - try (JsonReader labReader = JsonProviders.createReader(labInfoResponse)) { - labs = labReader.readArray(Lab::fromJson); - } - - if (labs.isEmpty()) { - log.error("No lab info found for labName: {}", user.getLabName()); - throw new RuntimeException("No lab info found for labName: " + user.getLabName()); - } - log.debug("Successfully parsed lab info for: {}", user.getLabName()); - - LabResponse response = new LabResponse(); - response.setUser(user); - response.setApp(labApps.get(0)); - response.setLab(labs.get(0)); - - log.info("Successfully created LabResponse for user: {}", user.getUpn()); - return response; - - } catch (Exception e) { - log.error("Failed to create LabResponse from API result: {}", e.getMessage(), e); - throw new RuntimeException("Failed to create LabResponse from API result", e); - } - } - - /** - * Execute a query against the lab API. - * - * @param query UserQueryHelper parameters - * @return JSON response string - */ - private String runQuery(UserQueryHelper query) { - Map queryDict = new HashMap<>(); - - // Building user query - required parameters set to defaults if not supplied - queryDict.put( - LabConstants.MULTI_FACTOR_AUTHENTICATION, - MFA.NONE.toString() - ); - - queryDict.put( - LabConstants.PROTECTION_POLICY, - ProtectionPolicy.NONE.toString() - ); - - if (query.getUserType() != null) { - queryDict.put(LabConstants.USER_TYPE, query.getUserType().toString()); - } - - if (query.getB2cIdentityProvider() != null) { - queryDict.put(LabConstants.B2C_PROVIDER, - query.getB2cIdentityProvider().toString()); - } - - if (query.getFederationProvider() != null) { - queryDict.put(LabConstants.FEDERATION_PROVIDER, - query.getFederationProvider().toString()); - } - - if (query.getSignInAudience() != null) { - queryDict.put(LabConstants.SIGN_IN_AUDIENCE, - query.getSignInAudience().toString()); - } - - if (query.getAzureEnvironment() != null) { - queryDict.put(LabConstants.AZURE_ENVIRONMENT, - query.getAzureEnvironment()); - } - - return sendLabRequest(LabConstants.LAB_ENDPOINT, queryDict); - } - - /** - * Send HTTP request to lab API with query parameters. - * - * @param requestUrl Base URL for the request - * @param queryDict Query parameters - * @return Response string - */ - private String sendLabRequest(String requestUrl, Map queryDict) { - try { - log.debug("Acquiring lab access token"); - String accessToken = getLabAccessToken(); - log.debug("Sending lab request to: {} with {} query parameters", requestUrl, queryDict.size()); - String response = LabHttpHelper.sendRequestToLab(requestUrl, queryDict, accessToken); - log.debug("Lab request successful, received {} characters", response.length()); - return response; - } catch (IOException e) { - log.error("Failed to send lab request to {}: {}", requestUrl, e.getMessage()); - throw new RuntimeException("Failed to send lab request", e); - } - } - - private void initLabApp() { - if (labApp == null) { - log.debug("Initializing lab app"); - KeyVaultSecretsProvider keyVaultSecretsProvider = new KeyVaultSecretsProvider(); - - String appID = keyVaultSecretsProvider.getSecretByName("LabVaultAppID").getValue(); - log.debug("Retrieved lab app ID from Key Vault"); - - try { - labApp = ConfidentialClientApplication.builder( - appID, - keyVaultSecretsProvider.getClientCredentialFromKeyStore()) - .authority(LabConstants.MICROSOFT_AUTHORITY) - .build(); - log.debug("Lab app initialized successfully"); - } catch (Exception e) { - log.error("Error initializing lab app: {}", e.getMessage(), e); - throw new RuntimeException("Error initializing lab app: " + e.getMessage()); - } - } - } - - private String getLabAccessToken() { - try { - initLabApp(); - - log.debug("Acquiring lab access token"); - IAuthenticationResult result = labApp.acquireToken( - ClientCredentialParameters - .builder(Collections.singleton(LabConstants.LAB_API_SCOPE)) - .build() - ).get(); - - log.debug("Successfully acquired lab access token"); - return result.accessToken(); - - } catch (Exception e) { - log.error("Error acquiring lab access token: {}", e.getMessage(), e); - throw new RuntimeException("Error acquiring lab access token: " + e.getMessage()); - } - } - - /** - * Execute HTTP GET request to lab API endpoint. - * - * @param address Full URL to request - * @return Response body as string - */ - String getLabResponse(String address) { - try { - log.debug("Getting lab response from: {}", address); - String accessToken = getLabAccessToken(); - String response = LabHttpHelper.sendRequestToLab(address, accessToken); - log.debug("Successfully retrieved lab response: {} characters", response.length()); - return response; - } catch (IOException e) { - log.error("Failed to get lab response from {}: {}", address, e.getMessage()); - throw new RuntimeException("Failed to get lab response from: " + address, e); - } - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/Lab.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfig.java similarity index 74% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/Lab.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfig.java index a2100468..f1ba16f9 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/Lab.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfig.java @@ -10,7 +10,10 @@ import java.io.IOException; -public class Lab implements JsonSerializable { +/** + * Represents a JSON response of Lab information. + */ +public class LabConfig implements JsonSerializable { private String labName; private String domain; private String tenantId; @@ -18,8 +21,8 @@ public class Lab implements JsonSerializable { private String azureEnvironment; private String authority; - static Lab fromJson(JsonReader jsonReader) throws IOException { - Lab lab = new Lab(); + static LabConfig fromJson(JsonReader jsonReader) throws IOException { + LabConfig labConfig = new LabConfig(); return jsonReader.readObject(reader -> { while (reader.nextToken() != JsonToken.END_OBJECT) { @@ -28,29 +31,29 @@ static Lab fromJson(JsonReader jsonReader) throws IOException { switch (fieldName) { case "labName": - lab.labName = reader.getString(); + labConfig.labName = reader.getString(); break; case "domain": - lab.domain = reader.getString(); + labConfig.domain = reader.getString(); break; case "tenantId": - lab.tenantId = reader.getString(); + labConfig.tenantId = reader.getString(); break; case "federationProvider": - lab.federationProvider = reader.getString(); + labConfig.federationProvider = reader.getString(); break; case "azureEnvironment": - lab.azureEnvironment = reader.getString(); + labConfig.azureEnvironment = reader.getString(); break; case "authority": - lab.authority = reader.getString(); + labConfig.authority = reader.getString(); break; default: reader.skipChildren(); break; } } - return lab; + return labConfig; }); } diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfigHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfigHelper.java new file mode 100644 index 00000000..d399b9d8 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfigHelper.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi; + +/** + * Helper class to provide configuration needed by integration tests, such as Azure user and app info. + * + * The returned LabResponse objects merge user, app, and lab configuration that represent a specific test scenario. + */ +public class LabConfigHelper { + + /** + * Most common configuration used by integration tests, meant for public cloud and managed user scenarios. + */ + public static LabResponse getDefaultConfig() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); + } + + /** + * Configuration for multi-tenant public client app scenarios. + * + * Avoids AADSTS7000218 credential errors in certain public client scenarios. + */ + public static LabResponse getMultiTenantAppPublicClientConfig() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgsPC-JSON"); + } + + /** + * Configuration for ADFS federated user scenarios. + */ + public static LabResponse getAdfsConfig() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); + } + + /** + * Configuration for B2C user scenarios. + */ + public static LabResponse getB2CConfig() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-USER-B2C-JSON", "ID4SLAB1", "MSAL-App-B2C-JSON"); + } + + /** + * Configuration for Arlington/US government cloud scenarios. + */ + public static LabResponse getArlingtonConfig() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-USER-Arlington-JSON", "ARLMSIDLAB1", "MSAL-App-Arlington-JSON"); + } + + /** + * Configuration for CIAM scenarios. + */ + public static LabResponse getCiamConfig() { + return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-USER-CIAM-JSON", "ARLMSIDLAB1", "MSAL-App-CIAM-JSON"); + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java deleted file mode 100644 index a93e64b5..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConstants.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -/** - * Constants for lab API endpoints and query parameters. - */ -class LabConstants { - - // Base endpoints - static final String LAB_ENDPOINT = "https://msidlab.com/api/user"; - static final String LAB_APP_ENDPOINT = "https://msidlab.com/api/app/"; - static final String LAB_INFO_ENDPOINT = "https://msidlab.com/api/Lab/"; - - // Query parameter keys - static final String USER_TYPE = "usertype"; - static final String MULTI_FACTOR_AUTHENTICATION = "mfa"; - static final String PROTECTION_POLICY = "protectionpolicy"; - static final String B2C_PROVIDER = "b2cprovider"; - static final String FEDERATION_PROVIDER = "federationprovider"; - static final String SIGN_IN_AUDIENCE = "SignInAudience"; - static final String AZURE_ENVIRONMENT = "azureenvironment"; - - static final String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; - static final String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; - - static final String LAB_API_SCOPE = "https://request.msidlab.com/.default"; -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java deleted file mode 100644 index f7f0b170..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabHttpHelper.java +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.ssl.HttpsURLConnection; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; -import java.util.Map; - -class LabHttpHelper { - - private static final Logger log = LoggerFactory.getLogger(LabHttpHelper.class); - - static String sendRequestToLab(String url, Map queryMap, String accessToken) throws IOException { - return sendRequestToLab(buildUrl(url, queryMap), accessToken); - } - - static String sendRequestToLab(String url, String accessToken) throws IOException { - return sendRequestToLab(new URL(url), accessToken); - } - - static String sendRequestToLab(URL labUrl, String accessToken) throws IOException { - log.debug("Sending HTTP GET request to: {}", labUrl); - - HttpsURLConnection conn = (HttpsURLConnection) labUrl.openConnection(); - - conn.setRequestProperty("Authorization", "Bearer " + accessToken); - conn.setRequestProperty("Accept", "application/json"); - conn.setReadTimeout(30000); // 30 seconds - conn.setConnectTimeout(30000); - - int responseCode = conn.getResponseCode(); - log.debug("HTTP response code: {}", responseCode); - - StringBuilder content = new StringBuilder(); - try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { - String inputLine; - while ((inputLine = in.readLine()) != null) { - content.append(inputLine); - } - } catch (IOException e) { - log.error("Failed to read response from {}: {}", labUrl, e.getMessage()); - throw e; - } finally { - conn.disconnect(); - } - - String response = content.toString(); - log.debug("HTTP response received: {} characters", response.length()); - - return response; - } - - static URL buildUrl(String url, Map queryMap) throws MalformedURLException { - if (queryMap.isEmpty()) { - return new URL(url); - } - - StringBuilder queryParameters = new StringBuilder(); - for (Map.Entry entry : queryMap.entrySet()) { - if (queryParameters.length() > 0) { - queryParameters.append("&"); - } - queryParameters.append(encodeUTF8(entry.getKey())) - .append("=") - .append(encodeUTF8(entry.getValue())); - } - - String urlString = url + "?" + queryParameters; - return new URL(urlString); - } - - private static String encodeUTF8(String s) { - try { - return URLEncoder.encode(s, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("Error: cannot encode query parameter " + s); - } - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java index d7ce82cf..4f7ff8f5 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java @@ -11,36 +11,36 @@ import java.io.IOException; /** - * Container for lab API response data containing user, app, and lab information. + * Container that represents JSON responses from our test infrastructure, such as user and app config needed by integration tests */ public class LabResponse implements JsonSerializable { - private LabUser user; - private LabApp app; - private Lab lab; + private UserConfig user; + private AppConfig app; + private LabConfig labConfig; - public LabUser getUser() { + public UserConfig getUser() { return user; } - void setUser(LabUser user) { + void setUser(UserConfig user) { this.user = user; } - public LabApp getApp() { + public AppConfig getApp() { return app; } - void setApp(LabApp app) { + void setApp(AppConfig app) { this.app = app; } - public Lab getLab() { - return lab; + public LabConfig getLab() { + return labConfig; } - void setLab(Lab lab) { - this.lab = lab; + void setLab(LabConfig labConfig) { + this.labConfig = labConfig; } /** @@ -51,18 +51,18 @@ static LabResponse fromJson(JsonReader jsonReader) throws IOException { LabResponse response = new LabResponse(); while (reader.nextToken() != JsonToken.END_OBJECT) { - String fieldName = reader.getFieldName(); + String fieldName = reader.getFieldName().toLowerCase(); reader.nextToken(); switch (fieldName) { case "user": - response.user = LabUser.fromJson(reader); + response.user = UserConfig.fromJson(reader); break; case "app": - response.app = LabApp.fromJson(reader); + response.app = AppConfig.fromJson(reader); break; case "lab": - response.lab = Lab.fromJson(reader); + response.labConfig = LabConfig.fromJson(reader); break; default: reader.skipChildren(); @@ -83,8 +83,8 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { if (app != null) { jsonWriter.writeJsonField("app", app); } - if (lab != null) { - jsonWriter.writeJsonField("lab", lab); + if (labConfig != null) { + jsonWriter.writeJsonField("labConfig", labConfig); } jsonWriter.writeEndObject(); @@ -93,9 +93,9 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { @Override public String toString() { - return String.format("LabResponse{user=%s, app=%s, lab=%s}", + return String.format("LabResponse{user=%s, app=%s, labConfig=%s}", user != null ? user.getUpn() : "null", app != null ? app.getAppId() : "null", - lab != null ? lab.getTenantId() : "null"); + labConfig != null ? labConfig.getTenantId() : "null"); } } \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java deleted file mode 100644 index cca6e759..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabServiceParameters.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -class LabServiceParameters { - - enum FederationProvider { - CIAMCUD // CIAM CUD - } - - enum B2CIdentityProvider { - LOCAL // Local B2C account - } - - enum UserType { - B2C, - CLOUD, - FEDERATED, - MSA - } - - enum MFA { - NONE - } - - enum ProtectionPolicy { - NONE - } - - enum SignInAudience { - AzureAdMyOrg - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java deleted file mode 100644 index 708b9f78..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserHelper.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -public class LabUserHelper { - - private static final LabApiService labService = new LabApiService(); - - public static LabResponse getDefaultUser() { - return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); - } - - public static LabResponse getDefaultUserMultiTenantApp() { - return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgs-JSON"); - } - - //Avoids AADSTS7000218 credential errors in certain public client scenarios - public static LabResponse getDefaultUserMultiTenantAppPublicClient() { - return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-User-Default-JSON", "ID4SLAB1", "MSAL-APP-AzureADMultipleOrgsPC-JSON"); - } - - public static LabResponse getDefaultAdfsUser() { - return KeyVaultRegistry.getMsalTeamProvider().mergeLabResponses("MSAL-USER-FedDefault-JSON", "ID4SLAB1", "MSAL-App-Default-JSON"); - } - - public static LabResponse getB2CLocalAccount() { - return labService.getLabUserData(UserQueryHelper.b2cLocalAccountQuery()); - } - - public static LabResponse getArlingtonUser() { - return labService.getLabUserData(UserQueryHelper.arlingtonUserQuery()); - } - - public static LabResponse getCiamCudUser() { - return labService.getLabUserData(UserQueryHelper.ciamCudUserQuery()); - } - - public static LabResponse getMSAUser() { - return labService.getLabUserData(UserQueryHelper.msaUserQuery()); - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java deleted file mode 100644 index 63a22601..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUserNotFoundException.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -/** - * Exception thrown when a lab user matching the query cannot be found. - */ -class LabUserNotFoundException extends RuntimeException { - - private final UserQueryHelper query; - - LabUserNotFoundException(UserQueryHelper query, String message) { - super(message); - this.query = query; - } - - @Override - public String toString() { - return String.format("LabUserNotFoundException{query=%s, message=%s}", - query, getMessage()); - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUser.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserConfig.java similarity index 96% rename from msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUser.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserConfig.java index 0f2fe0cb..38b78ab0 100644 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabUser.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserConfig.java @@ -10,7 +10,7 @@ import java.io.IOException; -public class LabUser implements JsonSerializable { +public class UserConfig implements JsonSerializable { private String appId; private String objectId; private String userType; @@ -29,8 +29,8 @@ public class LabUser implements JsonSerializable { private String password; private String federationProvider; - static LabUser fromJson(JsonReader jsonReader) throws IOException { - LabUser user = new LabUser(); + static UserConfig fromJson(JsonReader jsonReader) throws IOException { + UserConfig user = new UserConfig(); return jsonReader.readObject(reader -> { while (reader.nextToken() != JsonToken.END_OBJECT) { diff --git a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java deleted file mode 100644 index d6f6be57..00000000 --- a/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserQueryHelper.java +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.microsoft.aad.msal4j.labapi; - -import com.microsoft.aad.msal4j.labapi.LabServiceParameters.B2CIdentityProvider; -import com.microsoft.aad.msal4j.labapi.LabServiceParameters.FederationProvider; -import com.microsoft.aad.msal4j.labapi.LabServiceParameters.SignInAudience; -import com.microsoft.aad.msal4j.labapi.LabServiceParameters.UserType; - -class UserQueryHelper { - private UserType userType; - private B2CIdentityProvider b2cIdentityProvider; - private FederationProvider federationProvider; - private SignInAudience signInAudience; - private String azureEnvironment; - - UserType getUserType() { - return userType; - } - - void setUserType(UserType userType) { - this.userType = userType; - } - - B2CIdentityProvider getB2cIdentityProvider() { - return b2cIdentityProvider; - } - - void setB2cIdentityProvider(B2CIdentityProvider b2cIdentityProvider) { - this.b2cIdentityProvider = b2cIdentityProvider; - } - - FederationProvider getFederationProvider() { - return federationProvider; - } - - void setFederationProvider(FederationProvider federationProvider) { - this.federationProvider = federationProvider; - } - - String getAzureEnvironment() { - return azureEnvironment; - } - - void setAzureEnvironment(String azureEnvironment) { - this.azureEnvironment = azureEnvironment; - } - - SignInAudience getSignInAudience() { - return signInAudience; - } - - void setSignInAudience(SignInAudience signInAudience) { - this.signInAudience = signInAudience; - } - - static UserQueryHelper arlingtonUserQuery() { - UserQueryHelper query = new UserQueryHelper(); - query.setUserType(UserType.CLOUD); - query.setAzureEnvironment(AzureEnvironment.AZURE_US_GOVERNMENT); - return query; - } - - /** - * Gets a B2C local account (username/password) from the lab. - */ - static UserQueryHelper b2cLocalAccountQuery() { - UserQueryHelper query = new UserQueryHelper(); - query.setUserType(UserType.B2C); - query.setB2cIdentityProvider(B2CIdentityProvider.LOCAL); - return query; - } - - /** - * Gets a CIAM user with Custom User Domain (CUD). - * Example: login.customdomain.com instead of tenant.ciamlogin.com - */ - static UserQueryHelper ciamCudUserQuery() { - UserQueryHelper query = new UserQueryHelper(); - query.setFederationProvider(FederationProvider.CIAMCUD); - query.setSignInAudience(SignInAudience.AzureAdMyOrg); - return query; - } - - /** - * Gets a regular Microsoft Account (MSA) user, not tied to B2C. - * This is for consumer accounts like outlook.com, hotmail.com, etc. - */ - static UserQueryHelper msaUserQuery() { - UserQueryHelper query = new UserQueryHelper(); - query.setUserType(UserType.MSA); - return query; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + (userType != null ? userType.hashCode() : 0); - result = 31 * result + (azureEnvironment != null ? azureEnvironment.hashCode() : 0); - result = 31 * result + (b2cIdentityProvider != null ? b2cIdentityProvider.hashCode() : 0); - result = 31 * result + (federationProvider != null ? federationProvider.hashCode() : 0); - result = 31 * result + (signInAudience != null ? signInAudience.hashCode() : 0); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - - UserQueryHelper other = (UserQueryHelper) obj; - return java.util.Objects.equals(userType, other.userType) && - java.util.Objects.equals(azureEnvironment, other.azureEnvironment) && - java.util.Objects.equals(b2cIdentityProvider, other.b2cIdentityProvider) && - java.util.Objects.equals(federationProvider, other.federationProvider) && - java.util.Objects.equals(signInAudience, other.signInAudience); - } - - //Formats the fields as they would be seen in the query parameters section of a URL - @Override - public String toString() { - StringBuilder queryString = new StringBuilder(); - - if (userType != null) { - queryString.append("userType=").append(userType).append("&"); - } - - if (b2cIdentityProvider != null) { - queryString.append("b2cIdentityProvider=").append(b2cIdentityProvider).append("&"); - } - - if (federationProvider != null) { - queryString.append("federationProvider=").append(federationProvider).append("&"); - } - - if (signInAudience != null) { - queryString.append("signInAudience=").append(signInAudience).append("&"); - } - - if (azureEnvironment != null) { - queryString.append("azureEnvironment=").append(azureEnvironment).append("&"); - } - - // Remove trailing "&" if any parameters were added - if (queryString.length() > 0) { - queryString.setLength(queryString.length() - 1); - } - - return "?" + queryString; - } -} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java index 1d384b4f..53ed0fbc 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -4,7 +4,7 @@ package infrastructure; import com.microsoft.aad.msal4j.TestConstants; -import com.microsoft.aad.msal4j.labapi.LabUser; +import com.microsoft.aad.msal4j.labapi.UserConfig; import infrastructure.pageobjects.ADFSLoginPage; import infrastructure.pageobjects.AzureADLoginPage; import infrastructure.pageobjects.B2CLocalLoginPage; @@ -40,21 +40,21 @@ public static WebElement waitForElementToBeVisibleAndEnabled(WebDriver driver, B return wait.until(ExpectedConditions.elementToBeClickable(by)); } - public static void performADOrCiamLogin(WebDriver driver, LabUser user) { + public static void performADOrCiamLogin(WebDriver driver, UserConfig user) { LOG.info("performADOrCiamLogin for user: {}", user.getUpn()); AzureADLoginPage loginPage = new AzureADLoginPage(driver); loginPage.login(user.getUpn(), user.getPassword()); } - public static void performADFSLogin(WebDriver driver, LabUser user) { + public static void performADFSLogin(WebDriver driver, UserConfig user) { LOG.info("performADFSLogin for user: {}", user.getUpn()); ADFSLoginPage loginPage = new ADFSLoginPage(driver); loginPage.login(user.getUpn(), user.getPassword()); } - public static void performLocalLogin(WebDriver driver, LabUser user) { + public static void performLocalLogin(WebDriver driver, UserConfig user) { LOG.info("performLocalLogin"); B2CLocalLoginPage loginPage = new B2CLocalLoginPage(driver); @@ -70,7 +70,7 @@ public static void performLocalLogin(WebDriver driver, LabUser user) { * @param userCode The device code to enter * @param user The lab user credentials for login */ - public static void performDeviceCodeLogin(WebDriver driver, String verificationUri, String userCode, LabUser user) { + public static void performDeviceCodeLogin(WebDriver driver, String verificationUri, String userCode, UserConfig user) { LOG.info("performDeviceCodeLogin for user: {}", user.getUpn()); try { From ab33ff88135cbcd18da6187bb6bdf1a16d2a04a3 Mon Sep 17 00:00:00 2001 From: avdunn Date: Wed, 3 Dec 2025 09:53:12 -0800 Subject: [PATCH 14/14] Remove MSA test --- .../DeviceCodeIT.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java index 827ca255..e24cb50a 100644 --- a/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java +++ b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/DeviceCodeIT.java @@ -46,38 +46,6 @@ void DeviceCodeFlowADTest() throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - //TODO: This test is failing intermittently due to inconsistent login page layouts and is commented out until fixed. - //@Test() -// void DeviceCodeFlowMSATest() throws Exception { -// -// LabResponse labResponse = LabConfigHelper.getMSAUser(); -// UserConfig user = labResponse.getUser(); -// AppConfig app = labResponse.getApp(); -// -// PublicClientApplication pca = IntegrationTestHelper.createPublicApp(app.getAppId(), TestConstants.CONSUMERS_AUTHORITY); -// -// Consumer deviceCodeConsumer = (DeviceCode deviceCode) -> { -// runAutomatedDeviceCodeFlow(deviceCode, user); -// }; -// -// IAuthenticationResult result = pca.acquireToken(DeviceCodeFlowParameters -// .builder(Collections.singleton(""), -// deviceCodeConsumer) -// .build()) -// .get(); -// -// assertNotNull(result); -// assertNotNull(result.accessToken()); -// -// result = pca.acquireTokenSilently(SilentParameters. -// builder(Collections.singleton(""), result.account()). -// build()) -// .get(); -// -// assertNotNull(result); -// assertNotNull(result.accessToken()); -// } - private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, UserConfig user) { SeleniumExtensions.performDeviceCodeLogin( seleniumDriver,