diff --git a/msal4j-sdk/pom.xml b/msal4j-sdk/pom.xml index 15e4d751..b426cbbe 100644 --- a/msal4j-sdk/pom.xml +++ b/msal4j-sdk/pom.xml @@ -129,6 +129,18 @@ 2.14.0 test + + com.azure + azure-security-keyvault-certificates + 4.5.0 + test + + + com.azure + azure-identity + 1.10.0 + test + 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..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 @@ -3,17 +3,14 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; 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.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; @@ -29,14 +26,6 @@ class AcquireTokenInteractiveIT extends SeleniumTest { private static final Logger LOG = LoggerFactory.getLogger(AcquireTokenInteractiveIT.class); - private Config cfg; - - - @BeforeAll - public void setupUserProvider() { - setUpLapUserProvider(); - } - @AfterEach public void stopBrowser() { cleanUp(); @@ -47,79 +36,66 @@ public void startBrowser() { startUpBrowser(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenInteractive_ManagedUser(String environment) { - cfg = new Config(environment); - - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); - assertAcquireTokenCommon(user, cfg.commonAuthority(), cfg.graphDefaultScope()); - } + @Test + void acquireTokenInteractive_ManagedUser() { + LabResponse labResponse = LabConfigHelper.getMultiTenantAppPublicClientConfig(); + UserConfig user = labResponse.getUser(); - @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); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", TestConstants.GRAPH_DEFAULT_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); + @Test + void acquireTokenInteractive_Arlington() { + LabResponse labResponse = LabConfigHelper.getArlingtonConfig(); + UserConfig user = labResponse.getUser(); - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_2019); - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority()+ "common", TestConstants.GRAPH_DEFAULT_SCOPE); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") + @Test() @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - void acquireTokenInteractive_ADFSv4_Federated(String environment) { - cfg = new Config(environment); + void acquireTokenInteractive_ADFSv2022() { + LabResponse labResponse = LabConfigHelper.getAdfsConfig(); - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_4); - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope()); + UserConfig user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority() + "organizations/", TestConstants.ADFS_SCOPE); } - @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); - assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY); + @Test + void acquireTokenWithAuthorizationCode_B2C_Local() { + LabResponse labResponse = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); + assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY, labResponse.getApp().getAppId()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenWithAuthorizationCode_B2C_LegacyFormat(String environment) { - cfg = new Config(environment); - - User user = labUserProvider.getB2cUser(cfg.azureEnvironment, B2CProvider.LOCAL); - assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT); + @Test + void acquireTokenWithAuthorizationCode_B2C_LegacyFormat() { + LabResponse labResponse = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); + assertAcquireTokenB2C(user, TestConstants.B2C_AUTHORITY_LEGACY_FORMAT, labResponse.getApp().getAppId()); } @Test void acquireTokenInteractive_ManagedUser_InstanceAware() { - cfg = new Config(AzureEnvironment.AZURE); + LabResponse labResponse = LabConfigHelper.getArlingtonConfig(); + UserConfig user = labResponse.getUser(); - User user = labUserProvider.getDefaultUser(AzureEnvironment.AZURE_US_GOVERNMENT); - assertAcquireTokenInstanceAware(user); + assertAcquireTokenInstanceAware(user, labResponse.getApp().getAppId(), labResponse.getLab().getTenantId()); } @Test void acquireTokenInteractive_Ciam() { - User user = labUserProvider.getCiamCudUser(); + LabResponse labResponse = LabConfigHelper.getCiamConfig(); + UserConfig user = labResponse.getUser(); + AppConfig app = labResponse.getApp(); Map extraQueryParameters = new HashMap<>(); PublicClientApplication pca; try { pca = PublicClientApplication.builder( - user.getAppId()). - authority("https://" + user.getLabName() + ".ciamlogin.com/") + app.getAppId()). + authority(app.getAuthority()) .build(); } catch (MalformedURLException ex) { throw new RuntimeException(ex.getMessage()); @@ -153,8 +129,8 @@ void acquireTokenInteractive_Ciam() { assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenCommon(User user, String authority, String scope) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), authority); + private void assertAcquireTokenCommon(UserConfig user, String appId, String authority, String scope) { + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(appId, authority); IAuthenticationResult result = acquireTokenInteractive( user, @@ -165,26 +141,26 @@ 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(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(User user) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + user.getTenantID()); + 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, cfg.graphDefaultScope()); + IAuthenticationResult result = acquireTokenInteractive_instanceAware(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); assertEquals(user.getUpn(), result.account().username()); @@ -197,7 +173,7 @@ private void assertAcquireTokenInstanceAware(User user) { 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()); } @@ -213,7 +189,7 @@ private IAuthenticationResult acquireTokenSilently(IPublicClientApplication pca, } private IAuthenticationResult acquireTokenInteractive( - User user, + UserConfig user, PublicClientApplication pca, String scope) { @@ -243,7 +219,7 @@ private IAuthenticationResult acquireTokenInteractive( } private IAuthenticationResult acquireTokenInteractive_instanceAware( - User user, + UserConfig user, PublicClientApplication pca, String scope) { @@ -274,10 +250,10 @@ private IAuthenticationResult acquireTokenInteractive_instanceAware( class SeleniumOpenBrowserAction implements OpenBrowserAction { - private User user; + private UserConfig user; private PublicClientApplication pca; - SeleniumOpenBrowserAction(User 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 a649b348..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 @@ -3,13 +3,10 @@ package com.microsoft.aad.msal4j; -import labapi.*; +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; -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,48 +20,32 @@ @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") - void acquireTokenSilent_OrganizationAuthority_TokenRefreshed(String environment) throws Exception { - cfg = new Config(environment); - + @Test + void acquireTokenSilent_OrganizationAuthority_TokenRefreshed() throws Exception { // 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); } - @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 { // Access token should be returned from cache, and not using refresh token - - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); - + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + 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 @@ -74,23 +55,21 @@ 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); - - User user = labUserProvider.getDefaultUser(environment); + @Test + void acquireTokenSilent_ForceRefresh() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + 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 @@ -99,83 +78,30 @@ 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()); - } - - @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); - - 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); - - PublicClientApplication pca = PublicClientApplication.builder( - user.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()); + void acquireTokenSilent_usingOrganizationsAuthority_returnCachedAt() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); + + acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), TestConstants.ORGANIZATIONS_AUTHORITY, user); } @Test void acquireTokenSilent_usingTenantSpecificAuthority_returnCachedAt() throws Exception { - acquireTokenSilent_returnCachedTokens(cfg.tenantSpecificAuthority()); - } + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_ConfidentialClient_acquireTokenSilent(String environment) throws Exception { - cfg = new Config(environment); + acquireTokenSilent_returnCachedTokens(labResponse.getApp().getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId(), user); + } + @Test + void acquireTokenSilent_ConfidentialClient_acquireTokenSilent() throws Exception { 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(); @@ -186,7 +112,7 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilent(String environment String cachedAt = result.accessToken(); result = cca.acquireTokenSilently(SilentParameters - .builder(Collections.singleton(cfg.graphDefaultScope())) + .builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE)) .extraQueryParameters(extraParameters) .build()) .get(); @@ -198,7 +124,6 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilent(String environment @Test void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrowsException() throws Exception { - cfg = new Config(AzureEnvironment.AZURE); IConfidentialClientApplication cca = getConfidentialClientApplications(); @@ -212,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()); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_WithRefreshOn(String environment) throws Exception { - cfg = new Config(environment); - - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + @Test + void acquireTokenSilent_WithRefreshOn() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + 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); @@ -246,7 +169,7 @@ void acquireTokenSilent_WithRefreshOn(String environment) 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)); @@ -256,7 +179,7 @@ void acquireTokenSilent_WithRefreshOn(String environment) 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); @@ -264,47 +187,44 @@ 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); - - User user = labUserProvider.getDefaultUser(environment); + @Test + void acquireTokenSilent_TenantAsParameter() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + 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). - tenant(cfg.tenant()). + builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), account). + tenant(labResponse.getUser().getTenantId()). build()).get(); assertResultNotNull(resultWithTenantParam); assertTokensAreNotEqual(result, resultWithTenantParam); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void acquireTokenSilent_emptyStringScope(String environment) throws Exception { - cfg = new Config(environment); - User user = labUserProvider.getDefaultUser(environment); + @Test + void acquireTokenSilent_emptyStringScope() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); String emptyScope = StringHelper.EMPTY_STRING; @@ -317,16 +237,15 @@ 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); - User user = labUserProvider.getDefaultUser(environment); + @Test + void acquireTokenSilent_emptyScopeSet() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. @@ -349,13 +268,13 @@ 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 = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); Set scopes = new HashSet<>(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters. @@ -390,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 authority) throws Exception { - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + private void acquireTokenSilent_returnCachedTokens(String appId, String authority, UserConfig user) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + 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(); @@ -426,14 +343,15 @@ private void acquireTokenSilent_returnCachedTokens(String authority) throws Exce private IPublicClientApplication getPublicClientApplicationWithTokensInCache() throws Exception { - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); - acquireTokenUsernamePassword(user, pca, cfg.graphDefaultScope()); + acquireTokenUsernamePassword(user, pca, TestConstants.GRAPH_DEFAULT_SCOPE); return pca; } @@ -445,7 +363,7 @@ private IAuthenticationResult acquireTokenSilently(IPublicClientApplication pca, .get(); } - private IAuthenticationResult acquireTokenUsernamePassword(User 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 c0ed99a7..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 @@ -3,17 +3,14 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi.*; 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; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.MalformedURLException; @@ -30,13 +27,6 @@ class AuthorizationCodeIT extends SeleniumTest { private static final Logger LOG = LoggerFactory.getLogger(AuthorizationCodeIT.class); - private Config cfg; - - @BeforeAll - public void setupUserProvider() { - setUpLapUserProvider(); - } - @AfterEach public void stopBrowser() { cleanUp(); @@ -47,67 +37,34 @@ public void startBrowser() { startUpBrowser(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - public void acquireTokenWithAuthorizationCode_ManagedUser(String environment) { - cfg = new Config(environment); - - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); - assertAcquireTokenAAD(user, null); - } - @Test - @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") - public void acquireTokenWithAuthorizationCode_ADFSv2019_OnPrem() { - User user = labUserProvider.getOnPremAdfsUser(FederationProvider.ADFS_2019); - 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); - - User user = labUserProvider.getFederatedAdfsUser(cfg.azureEnvironment, FederationProvider.ADFS_2019); - assertAcquireTokenAAD(user, null); - } + public void acquireTokenWithAuthorizationCode_ManagedUser() { + LabResponse labResponse = LabConfigHelper.getMultiTenantAppPublicClientConfig(); + UserConfig user = labResponse.getUser(); - @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); + assertAcquireTokenAAD(user, labResponse.getApp().getAppId(), 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); + @Test + public void acquireTokenWithAuthorizationCode_B2C_Local() { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig 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(); + + LabResponse labResponse = LabConfigHelper.getCiamConfig(); + UserConfig user = labResponse.getUser(); + AppConfig app = labResponse.getApp(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + app.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 @@ -130,12 +87,21 @@ public void acquireTokenWithAuthorizationCode_CiamCud() throws Exception { assertEquals(resultSilent.account().username(), result.account().username()); } - private void assertAcquireTokenADFS2019(User user) { + @Test + @DisabledIfSystemProperty(named = "adfs.disabled", matches = "true") + void acquireTokenWithAuthorizationCode_ADFSv2022() { + LabResponse labResponse = LabConfigHelper.getAdfsConfig(); + + UserConfig user = labResponse.getUser(); + assertAcquireTokenADFS(user, labResponse.getApp().getAppId(), labResponse.getApp().getAuthority() + "organizations/"); + } + + private void assertAcquireTokenADFS(UserConfig 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()); @@ -151,24 +117,24 @@ private void assertAcquireTokenADFS2019(User user) { assertEquals(user.getUpn(), result.account().username()); } - private void assertAcquireTokenAAD(User user, Map> parameters) { + private void assertAcquireTokenAAD(UserConfig user, String appId, Map> parameters) { - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), 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()); } - private void assertAcquireTokenB2C(User user) { + private void assertAcquireTokenB2C(UserConfig user) { - String appId = LabService.getSecret(TestConstants.B2C_CONFIDENTIAL_CLIENT_LAB_APP_ID); - String appSecret = LabService.getSecret(TestConstants.B2C_CONFIDENTIAL_CLIENT_APP_SECRETID); + String appId = KeyVaultRegistry.getMsidLabProvider().getSecretByName(TestConstants.B2C_CONFIDENTIAL_CLIENT_LAB_APP_ID).getValue(); + String appSecret = KeyVaultRegistry.getMsidLabProvider().getSecretByName(TestConstants.B2C_CONFIDENTIAL_CLIENT_APP_SECRETID).getValue(); ConfidentialClientApplication cca; try { @@ -226,7 +192,7 @@ private IAuthenticationResult acquireTokenInteractiveB2C(ConfidentialClientAppli } private String acquireAuthorizationCodeAutomated( - User user, + UserConfig 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..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 @@ -3,54 +3,37 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi.*; 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); - } @Test void acquireTokenWithUsernamePassword_AzureGovernment() throws Exception { - assertAcquireTokenCommon(AzureEnvironment.AZURE_US_GOVERNMENT); - } - - private void assertAcquireTokenCommon(String azureEnvironment) throws Exception { - User user = labUserProvider.getUserByAzureEnvironment(azureEnvironment); - - App app = LabService.getApp(user.getAppId()); + LabResponse labResponse = LabConfigHelper.getArlingtonConfig(); + UserConfig user = labResponse.getUser(); + AppConfig app = labResponse.getApp(); PublicClientApplication pca = PublicClientApplication.builder( - user.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/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 9cca7017..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,10 +3,7 @@ package com.microsoft.aad.msal4j; -import labapi.AppCredentialProvider; -import labapi.AzureEnvironment; -import labapi.LabUserProvider; -import labapi.User; +import com.microsoft.aad.msal4j.labapi.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.BeforeAll; @@ -21,8 +18,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 +25,10 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ClientCredentialsIT { private IClientCertificate certificate; - private LabUserProvider labUserProvider; @BeforeAll void init() throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException, IOException { certificate = CertificateHelper.getClientCertificate(); - labUserProvider = LabUserProvider.getInstance(); } @Test @@ -46,8 +39,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 = KeyVaultRegistry.getMsidLabProvider().getSecretByName("LabVaultAppID").getValue(); IClientCredential credential = CertificateHelper.getClientCertificate(); assertAcquireTokenCommon(clientId, credential, TestConstants.MICROSOFT_AUTHORITY); @@ -64,29 +56,6 @@ 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_Certificate_CiamCud() throws Exception { String authorityCud = "https://login.msidlabsciam.com/fe362aec-5d43-45d1-b730-9755e60dc3b9/v2.0/"; @@ -132,8 +101,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 = KeyVaultRegistry.getMsidLabProvider().getSecretByName("LabVaultAppID").getValue(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder( clientId, CertificateHelper.getClientCertificate()). 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 94801e29..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 @@ -3,21 +3,17 @@ package com.microsoft.aad.msal4j; +import com.microsoft.aad.msal4j.labapi.*; import infrastructure.SeleniumExtensions; -import labapi.*; -import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; import org.slf4j.Logger; 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; + import java.util.Collections; import java.util.function.Consumer; @@ -25,28 +21,24 @@ 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(); } - @ParameterizedTest - @MethodSource("com.microsoft.aad.msal4j.EnvironmentsProvider#createData") - void DeviceCodeFlowADTest(String environment) throws Exception { - Config cfg = new Config(environment); - - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + @Test + void DeviceCodeFlowADTest() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); - PublicClientApplication pca = IntegrationTestHelper.createPublicApp(user.getAppId(), cfg.commonAuthority()); + PublicClientApplication pca = IntegrationTestHelper.createPublicApp(labResponse.getApp().getAppId(), TestConstants.MICROSOFT_AUTHORITY_HOST + labResponse.getUser().getTenantId()); Consumer 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(); @@ -54,107 +46,12 @@ 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. - //@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) { - 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"; - } - 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()); - - LOG.info("Loggin in ... click continue"); - WebElement continueBtn = SeleniumExtensions.waitForElementToBeVisibleAndEnable( - seleniumDriver, - new By.ById(continueButtonId)); - continueBtn.click(); - - if (isADFS2019) { - SeleniumExtensions.performADFS2019Login(seleniumDriver, user); - } else { - 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()); - } + private void runAutomatedDeviceCodeFlow(DeviceCode deviceCode, UserConfig user) { + SeleniumExtensions.performDeviceCodeLogin( + seleniumDriver, + deviceCode.verificationUri(), + deviceCode.userCode(), + user); } @AfterAll 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 f27c69f4..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}}; - } -} 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..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 @@ -3,11 +3,9 @@ package com.microsoft.aad.msal4j; -import labapi.LabUserProvider; -import labapi.User; +import com.microsoft.aad.msal4j.labapi.*; 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 = LabConfigHelper.getDefaultConfig(); + UserConfig 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 = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); + assertAcquireTokenCommon(user, labResponse.getApp().getAppId(), new ApacheHttpClientAdapter()); } @Test void acquireToken_readTimeout() throws Exception { - User user = labUserProvider.getDefaultUser(); + 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, 1); + assertAcquireTokenCommon_WithTimeout(user, labResponse.getApp().getAppId(), 1); } - private void assertAcquireTokenCommon(User user, IHttpClient httpClient) + private void assertAcquireTokenCommon(UserConfig 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(UserConfig 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..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 @@ -3,11 +3,9 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi.*; 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,43 +15,37 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) 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 { 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()). + 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(); 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(); + String clientId = TestConstants.OBO_CLIENT_ID; + String password = KeyVaultRegistry.getMsalTeamProvider().getSecretByName("IdentityDivisionDotNetOBOServiceSecret").getValue(); ConfidentialClientApplication cca = ConfidentialClientApplication.builder(clientId, ClientCredentialFactory.createFromSecret(password)). - authority(cfg.tenantSpecificAuthority()). + authority(TestConstants.MICROSOFT_AUTHORITY_HOST + "10c419d4-4a50-45b2-aa4e-919fb84df24f"). build(); IAuthenticationResult result1 = @@ -76,7 +68,7 @@ void acquireTokenWithOBO_testCache(String environment) 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(); @@ -86,7 +78,7 @@ void acquireTokenWithOBO_testCache(String environment) 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(); @@ -96,7 +88,7 @@ void acquireTokenWithOBO_testCache(String environment) throws Exception { IAuthenticationResult result5 = cca.acquireToken( OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(accessToken)) .skipCache(true) .build()). @@ -112,7 +104,7 @@ void acquireTokenWithOBO_testCache(String environment) throws Exception { IAuthenticationResult result6 = cca.acquireToken( OnBehalfOfParameters.builder( - Collections.singleton(cfg.graphDefaultScope()), + Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE), new UserAssertion(newAccessToken)) .build()). get(); @@ -130,14 +122,14 @@ private void assertResultNotNull(IAuthenticationResult result) { private String getAccessToken() throws Exception { - LabUserProvider labUserProvider = LabUserProvider.getInstance(); - User user = labUserProvider.getDefaultUser(cfg.azureEnvironment); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); - String clientId = cfg.appProvider.getAppId(); - String apiReadScope = cfg.appProvider.getOboAppIdURI() + "/user_impersonation"; + String clientId = TestConstants.OBO_CLIENT_ID; + String apiReadScope = TestConstants.OBO_APP_ID_URI + "/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/RefreshTokenIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/RefreshTokenIT.java index 37984846..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 @@ -3,36 +3,30 @@ package com.microsoft.aad.msal4j; -import labapi.LabUserProvider; -import labapi.User; +import com.microsoft.aad.msal4j.labapi.*; 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; import java.util.Collections; -import java.util.concurrent.ExecutionException; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class RefreshTokenIT { private String refreshToken; private PublicClientApplication pca; - private Config cfg; - - private void setUp(String environment) throws Exception { - LabUserProvider labUserProvider = LabUserProvider.getInstance(); - User user = labUserProvider.getDefaultUser(environment); + private void setUp() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); pca = PublicClientApplication.builder( - user.getAppId()). - authority(cfg.organizationsAuthority()). + labResponse.getApp().getAppId()). + 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,16 +35,13 @@ 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); - - setUp(environment); + @Test + void acquireTokenWithRefreshToken() throws Exception { + 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/SeleniumTest.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/SeleniumTest.java index 907e1192..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,26 +3,32 @@ package com.microsoft.aad.msal4j; +import com.microsoft.aad.msal4j.labapi.UserConfig; import infrastructure.SeleniumExtensions; -import labapi.B2CProvider; -import labapi.LabUserProvider; -import labapi.User; 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); - protected LabUserProvider labUserProvider; WebDriver seleniumDriver; HttpListener httpListener; - public void setUpLapUserProvider() { - labUserProvider = LabUserProvider.getInstance(); - } - 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()); + } } } @@ -30,26 +36,30 @@ public void startUpBrowser() { seleniumDriver = SeleniumExtensions.createDefaultWebDriver(); } - void runSeleniumAutomatedLogin(User user, AbstractClientApplicationBase app) { + void runSeleniumAutomatedLogin(UserConfig user, AbstractClientApplicationBase app) { AuthorityType authorityType = app.authenticationAuthority.authorityType; - if (authorityType == AuthorityType.B2C) { - switch (user.getB2cProvider().toLowerCase()) { - case B2CProvider.LOCAL: + + try { + switch (authorityType) { + case B2C: SeleniumExtensions.performLocalLogin(seleniumDriver, user); break; - case B2CProvider.GOOGLE: - SeleniumExtensions.performGoogleLogin(seleniumDriver, user); + case AAD: + SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); + break; + case ADFS: + SeleniumExtensions.performADFSLogin(seleniumDriver, user); break; - case B2CProvider.FACEBOOK: - SeleniumExtensions.performFacebookLogin(seleniumDriver, user); + case CIAM: + case OIDC: + SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); break; + default: + throw new IllegalArgumentException("Unsupported authority type: " + authorityType); } - } else if (authorityType == AuthorityType.AAD) { - SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); - } else if (authorityType == AuthorityType.ADFS) { - SeleniumExtensions.performADFS2019Login(seleniumDriver, user); - } else if (authorityType == AuthorityType.CIAM || authorityType == AuthorityType.OIDC) { - SeleniumExtensions.performADOrCiamLogin(seleniumDriver, user); + } 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/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/TokenCacheIT.java b/msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java index 7d5a48e7..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 @@ -3,36 +3,27 @@ package com.microsoft.aad.msal4j; -import labapi.*; +import com.microsoft.aad.msal4j.labapi.*; 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 = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). authority(TestConstants.ORGANIZATIONS_AUTHORITY). build(); @@ -59,65 +50,12 @@ 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()); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig guestUser = labResponse.getUser(); String dataToInitCache = TestHelper.readResource( this.getClass(), @@ -130,7 +68,7 @@ void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exce // acquire tokens for home tenant, and serialize cache PublicClientApplication pca = PublicClientApplication.builder( - guestUser.getAppId()). + labResponse.getApp().getAppId()). authority(TestConstants.ORGANIZATIONS_AUTHORITY) .setTokenCacheAccessAspect(persistenceAspect) .build(); @@ -142,11 +80,11 @@ void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exce .build()) .get(); - String guestTenantAuthority = TestConstants.MICROSOFT_AUTHORITY_HOST + lab.getTenantId(); + 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( - guestUser.getAppId()). + labResponse.getApp().getAppId()). authority(guestTenantAuthority). setTokenCacheAccessAspect(persistenceAspect). build(); @@ -174,32 +112,6 @@ void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exce "/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); - } - - 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 20d99f57..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 @@ -3,13 +3,9 @@ package com.microsoft.aad.msal4j; -import labapi.*; +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; -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; @@ -18,86 +14,32 @@ @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()); - } - - @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); - - 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); - - assertAcquireTokenCommon(user, cfg.organizationsAuthority(), cfg.graphDefaultScope(), user.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); - - 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()); + void acquireTokenWithUsernamePassword_Managed() throws Exception { + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + assertAcquireTokenCommon(labResponse.getUser(), TestConstants.ORGANIZATIONS_AUTHORITY, TestConstants.GRAPH_DEFAULT_SCOPE, labResponse.getApp().getAppId()); } @Test void acquireTokenWithUsernamePassword_AuthorityWithPort() throws Exception { - User user = labUserProvider.getDefaultUser(); + LabResponse labResponse = LabConfigHelper.getDefaultConfig(); + UserConfig user = labResponse.getUser(); assertAcquireTokenCommon( user, - TestConstants.COMMON_AUTHORITY_WITH_PORT, + TestConstants.MICROSOFT_AUTHORITY_HOST_WITH_PORT + user.getTenantId(), TestConstants.GRAPH_DEFAULT_SCOPE, - user.getAppId()); + labResponse.getApp().getAppId()); } @Test void acquireTokenWithUsernamePassword_Ciam() throws Exception { Map extraQueryParameters = new HashMap<>(); - User user = labUserProvider.getCiamCudUser(); - PublicClientApplication pca = PublicClientApplication.builder(user.getAppId()) + LabResponse labResponse = LabConfigHelper.getCiamConfig(); + UserConfig user = labResponse.getUser(); + + PublicClientApplication pca = PublicClientApplication.builder(labResponse.getApp().getAppId()) .authority("https://" + user.getLabName() + ".ciamlogin.com/") .build(); @@ -112,7 +54,7 @@ void acquireTokenWithUsernamePassword_Ciam() throws Exception { IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); } - private void assertAcquireTokenCommon(User user, String authority, String scope, String appId) + private void assertAcquireTokenCommon(UserConfig user, String authority, String scope, String appId) throws Exception { PublicClientApplication pca = PublicClientApplication.builder( @@ -125,7 +67,6 @@ private void assertAcquireTokenCommon(User user, String authority, String scope, user.getUpn(), user.getPassword().toCharArray()) .build()) - .get(); IntegrationTestHelper.assertAccessAndIdTokensNotNull(result); @@ -134,13 +75,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 = LabConfigHelper.getB2CConfig(); + UserConfig user = labResponse.getUser(); PublicClientApplication pca = PublicClientApplication.builder( - user.getAppId()). + labResponse.getApp().getAppId()). b2cAuthority(TestConstants.B2C_AUTHORITY_ROPC). build(); @@ -166,13 +105,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 = LabConfigHelper.getB2CConfig(); + UserConfig 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/labapi/App.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AppConfig.java similarity index 87% rename from msal4j-sdk/src/integrationtest/java/labapi/App.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AppConfig.java index eb918303..42eb3c98 100644 --- a/msal4j-sdk/src/integrationtest/java/labapi/App.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/AppConfig.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; @@ -10,7 +10,10 @@ import java.io.IOException; -public class App 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 App implements JsonSerializable { private String labName; private String clientSecret; - static App fromJson(JsonReader jsonReader) throws IOException { - App app = new App(); + static AppConfig fromJson(JsonReader jsonReader) throws IOException { + AppConfig app = new AppConfig(); return jsonReader.readObject(reader -> { while (reader.nextToken() != JsonToken.END_OBJECT) { @@ -80,8 +83,7 @@ public String getAuthority() { return authority; } - public String getClientSecret() { - return clientSecret; + public String getAppId() { + return appId; } - } \ No newline at end of file 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 new file mode 100644 index 00000000..34b7fd37 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultRegistry.java @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +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); + + 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; + } + + private KeyVaultRegistry() { + // Prevent instantiation + } +} + 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 new file mode 100644 index 00000000..204a662c --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/KeyVaultSecretsProvider.java @@ -0,0 +1,318 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.aad.msal4j.labapi; + +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; +import com.microsoft.aad.msal4j.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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; + +/** + * 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); + + /** + * The certificate alias used for authentication with Key Vault. + */ + public static final String CERTIFICATE_ALIAS = "LabAuth.MSIDLab.com"; + + static class KeyVaultInstance { + /** + * 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/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/"; + } + + 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) + */ + 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"); + } + + /** + * 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(CERTIFICATE_ALIAS, null); + publicCertificate = (X509Certificate) keystore.getCertificate(CERTIFICATE_ALIAS); + + 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); + } + + /** + * 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 RuntimeException("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 RuntimeException("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 RuntimeException("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 LabConfig 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() { + } +} \ 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/LabConfig.java similarity index 72% rename from msal4j-sdk/src/integrationtest/java/labapi/Lab.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfig.java index 5712d62d..f1ba16f9 100644 --- a/msal4j-sdk/src/integrationtest/java/labapi/Lab.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabConfig.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; @@ -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/LabResponse.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java new file mode 100644 index 00000000..4f7ff8f5 --- /dev/null +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/LabResponse.java @@ -0,0 +1,101 @@ +// 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; +import com.azure.json.JsonToken; +import com.azure.json.JsonWriter; + +import java.io.IOException; + +/** + * 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 UserConfig user; + private AppConfig app; + private LabConfig labConfig; + + public UserConfig getUser() { + return user; + } + + void setUser(UserConfig user) { + this.user = user; + } + + public AppConfig getApp() { + return app; + } + + void setApp(AppConfig app) { + this.app = app; + } + + public LabConfig getLab() { + return labConfig; + } + + void setLab(LabConfig labConfig) { + this.labConfig = labConfig; + } + + /** + * Deserialize a LabResponse from JSON. + */ + 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().toLowerCase(); + reader.nextToken(); + + switch (fieldName) { + case "user": + response.user = UserConfig.fromJson(reader); + break; + case "app": + response.app = AppConfig.fromJson(reader); + break; + case "lab": + response.labConfig = LabConfig.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 (labConfig != null) { + jsonWriter.writeJsonField("labConfig", labConfig); + } + + jsonWriter.writeEndObject(); + return jsonWriter; + } + + @Override + public String toString() { + return String.format("LabResponse{user=%s, app=%s, labConfig=%s}", + user != null ? user.getUpn() : "null", + app != null ? app.getAppId() : "null", + labConfig != null ? labConfig.getTenantId() : "null"); + } +} \ No newline at end of file diff --git a/msal4j-sdk/src/integrationtest/java/labapi/User.java b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserConfig.java similarity index 80% rename from msal4j-sdk/src/integrationtest/java/labapi/User.java rename to msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserConfig.java index 9c189e0f..38b78ab0 100644 --- a/msal4j-sdk/src/integrationtest/java/labapi/User.java +++ b/msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/labapi/UserConfig.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; @@ -10,7 +10,7 @@ import java.io.IOException; -public class User implements JsonSerializable { +public class UserConfig implements JsonSerializable { private String appId; private String objectId; private String userType; @@ -25,12 +25,12 @@ public class User implements JsonSerializable { private String labName; private String lastUpdatedBy; private String lastUpdatedDate; - private String tenantID; + private String tenantId; private String password; private String federationProvider; - static User fromJson(JsonReader jsonReader) throws IOException { - User user = new User(); + static UserConfig fromJson(JsonReader jsonReader) throws IOException { + UserConfig user = new UserConfig(); return jsonReader.readObject(reader -> { while (reader.nextToken() != JsonToken.END_OBJECT) { @@ -80,8 +80,11 @@ static User fromJson(JsonReader jsonReader) throws IOException { case "lastUpdatedDate": user.lastUpdatedDate = reader.getString(); break; - case "tenantID": - user.tenantID = reader.getString(); + case "tenantId": + user.tenantId = reader.getString(); + break; + case "federationProvider": + user.federationProvider = reader.getString(); break; default: reader.skipChildren(); @@ -110,7 +113,8 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException { jsonWriter.writeStringField("labName", labName); jsonWriter.writeStringField("lastUpdatedBy", lastUpdatedBy); jsonWriter.writeStringField("lastUpdatedDate", lastUpdatedDate); - jsonWriter.writeStringField("tenantID", tenantID); + jsonWriter.writeStringField("tenantId", tenantId); + jsonWriter.writeStringField("federationProvider", federationProvider); jsonWriter.writeEndObject(); @@ -121,51 +125,31 @@ 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 getLabName() { + return this.labName; } 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 getTenantId() { + return this.tenantId; } + /** + * Get the user's password, fetching from MSID Key Vault if necessary. + * + * @return The user's password + */ 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; + if (password == null || password.isEmpty()) { + password = KeyVaultRegistry.getMsidLabProvider().getUserPassword(getLabName()); + } + return password; } -} \ 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 99a461d0..00000000 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumConstants.java +++ /dev/null @@ -1,49 +0,0 @@ -//---------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -//------------------------------------------------------------------------------ - -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"; - - // 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 1eb04821..53ed0fbc 100644 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java +++ b/msal4j-sdk/src/integrationtest/java/infrastructure/SeleniumExtensions.java @@ -4,214 +4,97 @@ package infrastructure; import com.microsoft.aad.msal4j.TestConstants; -import labapi.User; -import org.apache.commons.io.FileUtils; +import com.microsoft.aad.msal4j.labapi.UserConfig; +import infrastructure.pageobjects.ADFSLoginPage; +import infrastructure.pageobjects.AzureADLoginPage; +import infrastructure.pageobjects.B2CLocalLoginPage; 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 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("--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 -> - { - try { - WebElement elementToBeDisplayed = driver.findElement(by); - if (elementToBeDisplayed.isDisplayed() && elementToBeDisplayed.isEnabled()) { - return elementToBeDisplayed; - } - return null; - } catch (StaleElementReferenceException e) { - LOG.info("Stale element waitForElementToBeVisibleAndEnable: " + e.getMessage()); - return null; - } - }); + public static WebElement waitForElementToBeVisibleAndEnabled(WebDriver driver, By by, Duration timeout) { + WebDriverWait wait = new WebDriverWait(driver, timeout.getSeconds()); + return wait.until(ExpectedConditions.elementToBeClickable(by)); } - public static WebElement waitForElementToBeVisibleAndEnable(WebDriver driver, By by) { - return waitForElementToBeVisibleAndEnable(driver, by, DEFAULT_TIMEOUT_IN_SEC); - } - - public static void performADOrCiamLogin(WebDriver driver, User user) { - LOG.info("performADOrCiamLogin"); - - UserInformationFields fields = new UserInformationFields(user); + public static void performADOrCiamLogin(WebDriver driver, UserConfig user) { + LOG.info("performADOrCiamLogin for user: {}", user.getUpn()); - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(fields.getAadUserNameInputId())).sendKeys(user.getUpn()); - - LOG.info("Loggin in ... Clicking after username"); - driver.findElement(new By.ById(fields.getAadSignInButtonId())).click(); - - LOG.info("Loggin in ... Entering password"); - By by = new By.ById(fields.getPasswordInputId()); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); - - LOG.info("Loggin in ... click submit"); - waitForElementToBeVisibleAndEnable(driver, new By.ById(fields.getPasswordSigInButtonId())). - click(); - - try { - checkAuthenticationCompletePage(driver); - return; - } catch (TimeoutException ex) { - LOG.error("Timeout Exception while checking authentication complete page: " + ex.getMessage()); - } - - LOG.info("Checking optional questions"); - - 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"); - - } catch (TimeoutException ex) { - LOG.error("Timeout Exception while checking sign in prompt: " + ex.getMessage()); - } - - 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"); - } catch (TimeoutException ex) { - LOG.error("Timeout Exception while checking stay signed in prompt: " + ex.getMessage()); - } + AzureADLoginPage loginPage = new AzureADLoginPage(driver); + loginPage.login(user.getUpn(), user.getPassword()); } - 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; - }); - } + public static void performADFSLogin(WebDriver driver, UserConfig user) { + LOG.info("performADFSLogin for user: {}", user.getUpn()); - public static void performADFS2019Login(WebDriver driver, User user) { - LOG.info("PerformADFS2019Login"); - - UserInformationFields fields = new UserInformationFields(user); - - LOG.info("Loggin in ... Entering username"); - driver.findElement(new By.ById(fields.getADFS2019UserNameInputId())).sendKeys(user.getUpn()); - - LOG.info("Loggin in ... Entering password"); - By by = new By.ById(fields.getPasswordInputId()); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); - - LOG.info("Loggin in ... click submit"); - waitForElementToBeVisibleAndEnable(driver, new By.ById(fields.getPasswordSigInButtonId())). - click(); - } - - public static void performLocalLogin(WebDriver driver, User user) { - LOG.info("PerformLocalLogin"); - - driver.findElement(new By.ById(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("Loggin in ... Entering password"); - By by = new By.ById(SeleniumConstants.B2C_LOCAL_PASSWORD_ID); - waitForElementToBeVisibleAndEnable(driver, by).sendKeys(user.getPassword()); - - waitForElementToBeVisibleAndEnable(driver, new By.ById(SeleniumConstants.B2C_LOCAL_SIGN_IN_BUTTON_ID)). - click(); + ADFSLoginPage loginPage = new ADFSLoginPage(driver); + loginPage.login(user.getUpn(), user.getPassword()); } - public static void performGoogleLogin(WebDriver driver, User user) { - LOG.info("PerformGoogleLogin"); + public static void performLocalLogin(WebDriver driver, UserConfig user) { + LOG.info("performLocalLogin"); - 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(); + B2CLocalLoginPage loginPage = new B2CLocalLoginPage(driver); + loginPage.login(TestConstants.B2C_UPN, user.getPassword()); } - public static void performFacebookLogin(WebDriver driver, User 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(); - } + /** + * 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, UserConfig user) { + LOG.info("performDeviceCodeLogin for user: {}", user.getUpn()); - 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()); + // 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(); + + // 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 73567824..00000000 --- a/msal4j-sdk/src/integrationtest/java/infrastructure/UserInformationFields.java +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package infrastructure; - -import labapi.FederationProvider; -import labapi.LabConstants; -import labapi.User; - -class UserInformationFields { - private final User user; - private String passwordInputId; - private String passwordSigInButtonId; - - UserInformationFields(User 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 getADFS2019UserNameInputId() { - return SeleniumConstants.ADFS2019_UPN_INPUT_ID; - } - - 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; - } - } -} 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"); + } +} 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/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"; -} 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 {