From 9140de8ffa11d1652a046a04a04b99f43ccc3aaa Mon Sep 17 00:00:00 2001 From: Avery-Dunn <62066438+Avery-Dunn@users.noreply.github.com> Date: Fri, 16 Sep 2022 13:23:25 -0700 Subject: [PATCH] Release 1.13.1 (#538) * docs: Improve syntax highlighting in README (#524) * fix: added Automatic-Module-Name to MANIFEST.MF * Documentation update for App token provider * Improvements and bug fixes for regional endpoint support (#535) * Improvements and bug fixes for regional endpoint support * Add links to region documentation * Address code review comments * Allow interactive request timeout to be configurable (#536) * Allow interactive request timeout to be configurable * Address code review comments * Address code review comments * Revert accidental commit to dev branch This reverts commit 5bca22fa7e15b4d2afb8aea5025ab52b7d49757c. * Version updates for 1.13.1 release (#537) Co-authored-by: Valery Yatsynovich Co-authored-by: Erwin Dupont Co-authored-by: Siddhi --- README.md | 10 +- bnd.bnd | 1 + changelog.txt | 6 + pom.xml | 2 +- .../ClientCredentialsIT.java | 60 ++++++ .../TestConstants.java | 2 + .../msal4j/AadInstanceDiscoveryProvider.java | 181 ++++++++++-------- .../msal4j/AbstractClientApplicationBase.java | 29 +-- ...AcquireTokenByInteractiveFlowSupplier.java | 12 +- .../aad/msal4j/ClientCredentialRequest.java | 3 + .../msal4j/ConfidentialClientApplication.java | 3 + .../msal4j/InteractiveRequestParameters.java | 9 + .../aad/msal4j/TokenRequestExecutor.java | 1 + src/samples/msal-b2c-web-sample/pom.xml | 2 +- src/samples/msal-obo-sample/pom.xml | 2 +- src/samples/msal-web-sample/pom.xml | 2 +- 16 files changed, 225 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index a654c852..30c112db 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Quick links: The library supports the following Java environments: - Java 8 (or higher) -Current version - 1.13.0 +Current version - 1.13.1 You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/master/changelog.txt). @@ -24,16 +24,18 @@ You can get the msal4j package through Maven or Gradle. ### Maven Find [the latest package in the Maven repository](https://mvnrepository.com/artifact/com.microsoft.azure/msal4j). -``` +```xml com.microsoft.azure msal4j - 1.13.0 + 1.13.1 ``` ### Gradle -compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.13.0' +```gradle +compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.13.1' +``` ## Usage diff --git a/bnd.bnd b/bnd.bnd index 9cb45beb..d1cf54b5 100644 --- a/bnd.bnd +++ b/bnd.bnd @@ -1 +1,2 @@ Export-Package: com.microsoft.aad.msal4j +Automatic-Module-Name: msal4j diff --git a/changelog.txt b/changelog.txt index 58f85000..ce01e93e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +Version 1.13.1 +============= +- Bug fixes and improvements for region API +- Allow configuration of timeouts for interactive requests +- Additional and more informative logging for regional scenarios and token requests in general + Version 1.13.0 ============= - Provide token caching functionality for managed identity tokens diff --git a/pom.xml b/pom.xml index 3ac97176..e075a083 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.microsoft.azure msal4j - 1.13.0 + 1.13.1 jar msal4j diff --git a/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java b/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java index 08dbc1fc..6e12baeb 100644 --- a/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java +++ b/src/integrationtest/java/com.microsoft.aad.msal4j/ClientCredentialsIT.java @@ -118,6 +118,13 @@ public void acquireTokenClientCredentials_DefaultCacheLookup() throws Exception Assert.assertNotEquals(result2.accessToken(), result3.accessToken()); } + @Test + public void acquireTokenClientCredentials_Regional() throws Exception { + String clientId = "2afb0add-2f32-4946-ac90-81a02aa4550e"; + + assertAcquireTokenCommon_withRegion(clientId, certificate); + } + private ClientAssertion getClientAssertion(String clientId) { return JwtHelper.buildJwt( clientId, @@ -156,4 +163,57 @@ private void assertAcquireTokenCommon_withParameters(String clientId, IClientCre Assert.assertNotNull(result); Assert.assertNotNull(result.accessToken()); } + + private void assertAcquireTokenCommon_withRegion(String clientId, IClientCredential credential) throws Exception { + ConfidentialClientApplication ccaNoRegion = ConfidentialClientApplication.builder( + clientId, credential). + authority(TestConstants.MICROSOFT_AUTHORITY). + build(); + + ConfidentialClientApplication ccaRegion = ConfidentialClientApplication.builder( + clientId, credential). + authority(TestConstants.MICROSOFT_AUTHORITY).azureRegion("westus"). + build(); + + //Ensure behavior when region not specified + IAuthenticationResult resultNoRegion = ccaNoRegion.acquireToken(ClientCredentialParameters + .builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE)) + .build()) + .get(); + + Assert.assertNotNull(resultNoRegion); + Assert.assertNotNull(resultNoRegion.accessToken()); + Assert.assertEquals(resultNoRegion.environment(), TestConstants.MICROSOFT_AUTHORITY_BASIC_HOST); + + //Ensure regional tokens are properly cached and retrievable + IAuthenticationResult resultRegion = ccaRegion.acquireToken(ClientCredentialParameters + .builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE)) + .build()) + .get(); + + Assert.assertNotNull(resultRegion); + Assert.assertNotNull(resultRegion.accessToken()); + Assert.assertEquals(resultRegion.environment(), TestConstants.REGIONAL_MICROSOFT_AUTHORITY_BASIC_HOST_WESTUS); + + IAuthenticationResult resultRegionCached = ccaRegion.acquireToken(ClientCredentialParameters + .builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE)) + .build()) + .get(); + + Assert.assertNotNull(resultRegionCached); + Assert.assertNotNull(resultRegionCached.accessToken()); + Assert.assertEquals(resultRegionCached.accessToken(), resultRegion.accessToken()); + + //Tokens retrieved from regional endpoints should be interchangeable with non-regional, and vice-versa + //For example, if an application doesn't configure a region but gets regional tokens added to its cache, they should be retrievable + ccaNoRegion.tokenCache = ccaRegion.tokenCache; + resultNoRegion = ccaNoRegion.acquireToken(ClientCredentialParameters + .builder(Collections.singleton(KEYVAULT_DEFAULT_SCOPE)) + .build()) + .get(); + + Assert.assertNotNull(resultNoRegion); + Assert.assertNotNull(resultNoRegion.accessToken()); + Assert.assertEquals(resultNoRegion.accessToken(), resultRegion.accessToken()); + } } diff --git a/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java b/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java index 85e93721..950109dc 100644 --- a/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java +++ b/src/integrationtest/java/com.microsoft.aad.msal4j/TestConstants.java @@ -18,6 +18,7 @@ public class TestConstants { public final static String B2C_CONFIDENTIAL_CLIENT_LAB_APP_ID = "MSIDLABB2C-MSAapp-AppID"; public final static String MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.com/"; + public final static String MICROSOFT_AUTHORITY_BASIC_HOST = "login.microsoftonline.com"; public final static String MICROSOFT_AUTHORITY_HOST_WITH_PORT = "https://login.microsoftonline.com:443/"; public final static String ARLINGTON_MICROSOFT_AUTHORITY_HOST = "https://login.microsoftonline.us/"; public final static String MICROSOFT_AUTHORITY_TENANT = "msidlab4.onmicrosoft.com"; @@ -29,6 +30,7 @@ public class TestConstants { public final static String COMMON_AUTHORITY_WITH_PORT = MICROSOFT_AUTHORITY_HOST_WITH_PORT + "msidlab4.onmicrosoft.com"; public final static String MICROSOFT_AUTHORITY = MICROSOFT_AUTHORITY_HOST + "microsoft.onmicrosoft.com"; public final static String TENANT_SPECIFIC_AUTHORITY = MICROSOFT_AUTHORITY_HOST + MICROSOFT_AUTHORITY_TENANT; + public final static String REGIONAL_MICROSOFT_AUTHORITY_BASIC_HOST_WESTUS = "westus.r." + MICROSOFT_AUTHORITY_BASIC_HOST; public final static String ARLINGTON_ORGANIZATIONS_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "organizations/"; public final static String ARLINGTON_COMMON_AUTHORITY = ARLINGTON_MICROSOFT_AUTHORITY_HOST + "common/"; diff --git a/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java b/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java index 32fc9131..11b2628b 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java +++ b/src/main/java/com/microsoft/aad/msal4j/AadInstanceDiscoveryProvider.java @@ -3,14 +3,13 @@ package com.microsoft.aad.msal4j; -import com.nimbusds.oauth2.sdk.http.HTTPResponse; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URL; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import java.util.TreeSet; import java.util.Map; @@ -22,9 +21,9 @@ class AadInstanceDiscoveryProvider { private final static String DEFAULT_TRUSTED_HOST = "login.microsoftonline.com"; private final static String AUTHORIZE_ENDPOINT_TEMPLATE = "https://{host}/{tenant}/oauth2/v2.0/authorize"; private final static String INSTANCE_DISCOVERY_ENDPOINT_TEMPLATE = "https://{host}:{port}/common/discovery/instance"; - private final static String INSTANCE_DISCOVERY_ENDPOINT_TEMPLATE_WITH_REGION = "https://{region}.r.{host}:{port}/common/discovery/instance"; - private final static String INSTANCE_DISCOVERY_SOVEREIGN_ENDPOINT_TEMPLATE_WITH_REGION = "https://{region}.{host}:{port}/common/discovery/instance"; private final static String INSTANCE_DISCOVERY_REQUEST_PARAMETERS_TEMPLATE = "?api-version=1.1&authorization_endpoint={authorizeEndpoint}"; + private final static String HOST_TEMPLATE_WITH_REGION = "{region}.r.{host}"; + private final static String SOVEREIGN_HOST_TEMPLATE_WITH_REGION = "{region}.{host}"; private final static String REGION_NAME = "REGION_NAME"; private final static int PORT_NOT_SET = -1; // For information of the current api-version refer: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service#versioning @@ -56,14 +55,35 @@ static InstanceDiscoveryMetadataEntry getMetadataEntry(URL authorityUrl, boolean validateAuthority, MsalRequest msalRequest, ServiceBundle serviceBundle) { + String host = authorityUrl.getHost(); + + if (shouldUseRegionalEndpoint(msalRequest)) { + //Server side telemetry requires the result from region discovery when any part of the region API is used + String detectedRegion = discoverRegion(msalRequest, serviceBundle); + + if (msalRequest.application().azureRegion() != null) { + host = getRegionalizedHost(authorityUrl.getHost(), msalRequest.application().azureRegion()); + } + + //If region autodetection is enabled and a specific region not already set, + // set the application's region to the discovered region so that future requests can skip the IMDS endpoint call + if (msalRequest.application().azureRegion() == null && msalRequest.application().autoDetectRegion()) { + if (detectedRegion != null) { + msalRequest.application().azureRegion = detectedRegion; + } + } + cacheRegionInstanceMetadata(authorityUrl.getHost(), msalRequest.application().azureRegion()); + serviceBundle.getServerSideTelemetry().getCurrentRequest().regionOutcome( + determineRegionOutcome(detectedRegion, msalRequest.application().azureRegion(), msalRequest.application().autoDetectRegion())); + } - InstanceDiscoveryMetadataEntry result = cache.get(authorityUrl.getHost()); + InstanceDiscoveryMetadataEntry result = cache.get(host); if (result == null) { doInstanceDiscoveryAndCache(authorityUrl, validateAuthority, msalRequest, serviceBundle); } - return cache.get(authorityUrl.getHost()); + return cache.get(host); } static Set getAliases(String host) { @@ -108,6 +128,68 @@ static void cacheInstanceDiscoveryMetadata(String host, build()); } + + private static boolean shouldUseRegionalEndpoint(MsalRequest msalRequest){ + if (msalRequest.application().azureRegion() != null || msalRequest.application().autoDetectRegion()){ + //This class type check is a quick and dirty fix to accommodate changes to the internal workings of the region API + // + //ESTS-R only supports a small, but growing, number of scenarios, and the original design failed silently whenever + // regions could not be used. To avoid a breaking change this check will allow supported flows to use regions, + // and unsupported flows will continue to fall back to global, but with an added warning and a link to more info + if (msalRequest.getClass() == ClientCredentialRequest.class) { + return true; + } else { + //Avoid unnecessary warnings when looking for cached tokens by checking if request was a silent call + if (msalRequest.getClass() != SilentRequest.class) { + log.warn("Regional endpoints are only available for client credential flow, request will fall back to using the global endpoint. See here for more information about supported scenarios: https://aka.ms/msal4j-azure-regions"); + } + return false; + } + } + return false; + } + + static void cacheRegionInstanceMetadata(String host, String region) { + + Set aliases = new HashSet<>(); + aliases.add(host); + String regionalHost = getRegionalizedHost(host, region); + + cache.putIfAbsent(regionalHost, InstanceDiscoveryMetadataEntry.builder(). + preferredCache(host). + preferredNetwork(regionalHost). + aliases(aliases). + build()); + } + + private static String getRegionalizedHost(String host, String region) { + String regionalizedHost; + + if (region == null){ + return host; + } + + //Cached calls may already have the regionalized authority, so if region info is already in the host just return as-is + if (host.contains(region)) { + return host; + } + + //Basic Microsoft authority hosts (login.microsoftonline.com, login.windows.net, etc.) follow one regional URL template, + // whereas sovereign cloud endpoints and any non-Microsoft authorities are assumed to follow another template + if (TRUSTED_HOSTS_SET.contains(host) && !TRUSTED_SOVEREIGN_HOSTS_SET.contains(host)){ + regionalizedHost = HOST_TEMPLATE_WITH_REGION. + replace("{region}", region). + replace("{host}", host); + + } else { + regionalizedHost = SOVEREIGN_HOST_TEMPLATE_WITH_REGION. + replace("{region}", region). + replace("{host}", host); + } + + return regionalizedHost; + } + private static String getAuthorizeEndpoint(String host, String tenant) { return AUTHORIZE_ENDPOINT_TEMPLATE. replace("{host}", host). @@ -129,101 +211,40 @@ private static String getInstanceDiscoveryEndpoint(URL authorityUrl) { replace("{port}", String.valueOf(port)); } - private static String getInstanceDiscoveryEndpointWithRegion(URL authorityUrl, String region) { - - String discoveryHost = TRUSTED_HOSTS_SET.contains(authorityUrl.getHost()) ? - authorityUrl.getHost() : - DEFAULT_TRUSTED_HOST; - - int port = authorityUrl.getPort() == PORT_NOT_SET ? - authorityUrl.getDefaultPort() : - authorityUrl.getPort(); - - if (TRUSTED_SOVEREIGN_HOSTS_SET.contains(authorityUrl.getHost())) { - return INSTANCE_DISCOVERY_SOVEREIGN_ENDPOINT_TEMPLATE_WITH_REGION. - replace("{region}", region). - replace("{host}", discoveryHost). - replace("{port}", String.valueOf(port)); - } else { - return INSTANCE_DISCOVERY_ENDPOINT_TEMPLATE_WITH_REGION. - replace("{region}", region). - replace("{host}", discoveryHost). - replace("{port}", String.valueOf(port)); - } - } - - private static AadInstanceDiscoveryResponse sendInstanceDiscoveryRequest(URL authorityUrl, MsalRequest msalRequest, ServiceBundle serviceBundle) { IHttpResponse httpResponse = null; - String providedRegion = msalRequest.application().azureRegion(); - String detectedRegion = null; - int regionOutcomeTelemetryValue = 0; - String regionToUse = null; - - //If a region was provided by a developer or they set the autoDetectRegion parameter, - // attempt to discover the region and set telemetry info based on the outcome - if (providedRegion != null) { - detectedRegion = discoverRegion(msalRequest, serviceBundle); - regionToUse = providedRegion; - regionOutcomeTelemetryValue = determineRegionOutcome(detectedRegion, providedRegion, msalRequest.application().autoDetectRegion()); - } else if (msalRequest.application().autoDetectRegion()) { - detectedRegion = discoverRegion(msalRequest, serviceBundle); - - if (detectedRegion != null) { - regionToUse = detectedRegion; - } - - regionOutcomeTelemetryValue = determineRegionOutcome(detectedRegion, providedRegion, msalRequest.application().autoDetectRegion()); - } - - //If the region is known, attempt to make instance discovery request with region endpoint - if (regionToUse != null) { - String instanceDiscoveryRequestUrl = getInstanceDiscoveryEndpointWithRegion(authorityUrl, regionToUse) + - formInstanceDiscoveryParameters(authorityUrl); - try { - httpResponse = executeRequest(instanceDiscoveryRequestUrl, msalRequest.headers().getReadonlyHeaderMap(), msalRequest, serviceBundle); - } catch (MsalClientException ex) { - log.warn("Could not retrieve regional instance discovery metadata, falling back to global endpoint"); - } - } + String instanceDiscoveryRequestUrl = getInstanceDiscoveryEndpoint(authorityUrl) + + formInstanceDiscoveryParameters(authorityUrl); - //If the region is unknown or the instance discovery failed at the region endpoint, try the global endpoint - if ((detectedRegion == null && providedRegion == null) || httpResponse == null || httpResponse.statusCode() != HTTPResponse.SC_OK) { - - String instanceDiscoveryRequestUrl = getInstanceDiscoveryEndpoint(authorityUrl) + - formInstanceDiscoveryParameters(authorityUrl); - - httpResponse = executeRequest(instanceDiscoveryRequestUrl, msalRequest.headers().getReadonlyHeaderMap(), msalRequest, serviceBundle); - } + httpResponse = executeRequest(instanceDiscoveryRequestUrl, msalRequest.headers().getReadonlyHeaderMap(), msalRequest, serviceBundle); if (httpResponse.statusCode() != HttpHelper.HTTP_STATUS_200) { throw MsalServiceExceptionFactory.fromHttpResponse(httpResponse); } - serviceBundle.getServerSideTelemetry().getCurrentRequest().regionOutcome(regionOutcomeTelemetryValue); return JsonHelper.convertJsonToObject(httpResponse.body(), AadInstanceDiscoveryResponse.class); } private static int determineRegionOutcome(String detectedRegion, String providedRegion, boolean autoDetect) { - int regionOutcomeTelemetryValue = 0; - if (providedRegion != null) { - if (detectedRegion == null) { + int regionOutcomeTelemetryValue = 0;//By default, assume region API was not used + if (providedRegion != null) {//Developer provided a region + if (detectedRegion == null) {//Region autodetection failed regionOutcomeTelemetryValue = RegionTelemetry.REGION_OUTCOME_DEVELOPER_AUTODETECT_FAILED.telemetryValue; - } else if (providedRegion.equals(detectedRegion)) { + } else if (providedRegion.equals(detectedRegion)) {//Provided and detected regions match regionOutcomeTelemetryValue = RegionTelemetry.REGION_OUTCOME_DEVELOPER_AUTODETECT_MATCH.telemetryValue; - } else { + } else {//Mismatch between provided and detected regions regionOutcomeTelemetryValue = RegionTelemetry.REGION_OUTCOME_DEVELOPER_AUTODETECT_MISMATCH.telemetryValue; } - } else if (autoDetect) { - if (detectedRegion != null) { - regionOutcomeTelemetryValue = RegionTelemetry.REGION_OUTCOME_AUTODETECT_SUCCESS.telemetryValue; - } else { + } else if (autoDetect) {//Developer enabled region autodetection + if (detectedRegion == null) {//Region autodetection failed regionOutcomeTelemetryValue = RegionTelemetry.REGION_OUTCOME_AUTODETECT_FAILED.telemetryValue; + } else {//Region autodetection succeeded + regionOutcomeTelemetryValue = RegionTelemetry.REGION_OUTCOME_AUTODETECT_SUCCESS.telemetryValue; } } @@ -280,6 +301,8 @@ private static String discoverRegion(MsalRequest msalRequest, ServiceBundle serv return null; } catch (Exception e) { //IMDS call failed, cannot find region + //The IMDS endpoint is only available from within an Azure environment, so the most common cause of this + // exception will likely be java.net.SocketException: Network is unreachable: connect log.warn(String.format("Exception during call to local IMDS endpoint: %s", e.getMessage())); currentRequest.regionSource(RegionTelemetry.REGION_SOURCE_FAILED_AUTODETECT.telemetryValue); diff --git a/src/main/java/com/microsoft/aad/msal4j/AbstractClientApplicationBase.java b/src/main/java/com/microsoft/aad/msal4j/AbstractClientApplicationBase.java index b16249b4..862a3ce8 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AbstractClientApplicationBase.java +++ b/src/main/java/com/microsoft/aad/msal4j/AbstractClientApplicationBase.java @@ -102,7 +102,7 @@ public abstract class AbstractClientApplicationBase implements IClientApplicatio @Accessors(fluent = true) @Getter - private String azureRegion; + protected String azureRegion; @Override public CompletableFuture acquireToken(AuthorizationCodeParameters parameters) { @@ -607,11 +607,13 @@ public T clientCapabilities(Set capabilities) { /** * Indicates that the library should attempt to discover the Azure region the application is running in when - * fetching the instance discovery metadata. - *

- * If the region is found, token requests will be sent to the regional ESTS endpoint rather than the global endpoint. - * If region information could not be found, the library will fall back to using the global endpoint, which is also - * the default behavior if this value is not set. + * fetching the instance discovery metadata. Regions can only be detected when running in an Azure environment, + * such as an Azure VM or other service, or if the environment has environment variable named REGION_NAME configured. + * + * Although you can enable both autodetection here and a specific region with {@link AbstractClientApplicationBase#azureRegion} at the same time, + * the region set with {@link AbstractClientApplicationBase#azureRegion} will take priority if there is a mismatch. + * + * See here for more information about supported scenarios: https://aka.ms/msal4j-azure-regions * * @param val boolean (default is false) * @return instance of the Builder on which method was called @@ -622,11 +624,16 @@ public T autoDetectRegion(boolean val) { } /** - * Indicates that the library should attempt to fetch the instance discovery metadata from the specified Azure region. - *

- * If the region is valid, token requests will be sent to the regional ESTS endpoint rather than the global endpoint. - * If region information could not be verified, the library will fall back to using the global endpoint, which is also - * the default behavior if this value is not set. + * Set the region that the library will use to format authorities in token requests. If given a valid Azure region, + * the library will attempt to make token requests at a regional ESTS-R endpoint rather than the global ESTS endpoint. + * + * Regions must be valid Azure regions and their short names should be used, such as 'westus' for the West US Azure region, + * 'centralus' for the Central US Azure region, etc. + * + * Although you can set a specific region here and enable autodetection with {@link AbstractClientApplicationBase#autoDetectRegion} at the same time + * the specific region set here will take priority over the autodetected region if there is a mismatch. + * + * See here for more information about supported scenarios: https://aka.ms/msal4j-azure-regions * * @param val String region name * @return instance of the Builder on which method was called diff --git a/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByInteractiveFlowSupplier.java b/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByInteractiveFlowSupplier.java index 68ababd0..f9dadf71 100644 --- a/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByInteractiveFlowSupplier.java +++ b/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByInteractiveFlowSupplier.java @@ -124,8 +124,16 @@ private void openDefaultSystemBrowser(URL url) { private AuthorizationResult getAuthorizationResultFromHttpListener() { AuthorizationResult result = null; try { - LOG.debug("Listening for authorization result"); - long expirationTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + 120; + int timeFromParameters = interactiveRequest.interactiveRequestParameters().httpPollingTimeoutInSeconds(); + long expirationTime; + + if (timeFromParameters > 0) { + LOG.debug(String.format("Listening for authorization result. Listener will timeout after %S seconds.", timeFromParameters)); + expirationTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + timeFromParameters; + } else { + LOG.warn("Listening for authorization result. Timeout configured to less than 1 second, listener will use a 1 second timeout instead."); + expirationTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + 1; + } while (result == null && !interactiveRequest.futureReference().get().isCancelled() && TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) < expirationTime) { diff --git a/src/main/java/com/microsoft/aad/msal4j/ClientCredentialRequest.java b/src/main/java/com/microsoft/aad/msal4j/ClientCredentialRequest.java index 51b5b1e2..56de5f6c 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ClientCredentialRequest.java +++ b/src/main/java/com/microsoft/aad/msal4j/ClientCredentialRequest.java @@ -11,6 +11,9 @@ class ClientCredentialRequest extends MsalRequest { ClientCredentialParameters parameters; + /** AppTokenProvider creates a Credential from a function that provides access tokens. The function + must be concurrency safe. This is intended only to allow the Azure SDK to cache MSI tokens. It isn't + useful to applications in general because the token provider must implement all authentication logic. */ Function> appTokenProvider; ClientCredentialRequest(ClientCredentialParameters parameters, diff --git a/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java b/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java index 0b418c05..78744796 100644 --- a/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java +++ b/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java @@ -34,6 +34,9 @@ public class ConfidentialClientApplication extends AbstractClientApplicationBase private boolean clientCertAuthentication = false; private ClientCertificate clientCertificate; + /** AppTokenProvider creates a Credential from a function that provides access tokens. The function + must be concurrency safe. This is intended only to allow the Azure SDK to cache MSI tokens. It isn't + useful to applications in general because the token provider must implement all authentication logic. */ public Function> appTokenProvider; @Accessors(fluent = true) diff --git a/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java b/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java index a5e2445f..acdb638a 100644 --- a/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java +++ b/src/main/java/com/microsoft/aad/msal4j/InteractiveRequestParameters.java @@ -85,6 +85,15 @@ public class InteractiveRequestParameters implements IAcquireTokenParameters { */ private String tenant; + /** + * The amount of time in seconds that the library will wait for an authentication result. 120 seconds is the default timeout, + * unless overridden here with some other positive integer + * + * If this timeout is set to 0 or less it will be ignored, and the library will use a 1 second timeout instead + */ + @Builder.Default + private int httpPollingTimeoutInSeconds = 120; + /** * If set to true, the authorization result will contain the authority for the user's home cloud, and this authority * will be used for the token request instead of the authority set in the application. diff --git a/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java b/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java index 8dbfa7f6..a8ab5194 100644 --- a/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java +++ b/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java @@ -34,6 +34,7 @@ class TokenRequestExecutor { AuthenticationResult executeTokenRequest() throws ParseException, IOException { + log.debug("Sending token request to: " + requestAuthority.canonicalAuthorityUrl()); OAuthHttpRequest oAuthHttpRequest = createOauthHttpRequest(); HTTPResponse oauthHttpResponse = oAuthHttpRequest.send(); return createAuthenticationResultFromOauthHttpResponse(oauthHttpResponse); diff --git a/src/samples/msal-b2c-web-sample/pom.xml b/src/samples/msal-b2c-web-sample/pom.xml index f4afdb35..a63252b5 100644 --- a/src/samples/msal-b2c-web-sample/pom.xml +++ b/src/samples/msal-b2c-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.0 + 1.13.1 com.nimbusds diff --git a/src/samples/msal-obo-sample/pom.xml b/src/samples/msal-obo-sample/pom.xml index 065d3750..68d81077 100644 --- a/src/samples/msal-obo-sample/pom.xml +++ b/src/samples/msal-obo-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.0 + 1.13.1 com.nimbusds diff --git a/src/samples/msal-web-sample/pom.xml b/src/samples/msal-web-sample/pom.xml index 7d5a712e..891c6aed 100644 --- a/src/samples/msal-web-sample/pom.xml +++ b/src/samples/msal-web-sample/pom.xml @@ -23,7 +23,7 @@ com.microsoft.azure msal4j - 1.13.0 + 1.13.1 com.nimbusds