Skip to content

Commit efee88f

Browse files
authored
Assorted fixes (#684)
* Remove default timeouts and improve exception messages * Fix NPE for on-prem ADFS scenario * Log MSAL message but re-throw exception * Update vulnerable test dependency
1 parent 1dfb59c commit efee88f

File tree

7 files changed

+92
-11
lines changed

7 files changed

+92
-11
lines changed

msal4j-sdk/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
<dependency>
132132
<groupId>com.google.guava</groupId>
133133
<artifactId>guava</artifactId>
134-
<version>32.0.0-jre</version>
134+
<version>32.1.1-jre</version>
135135
<scope>test</scope>
136136
</dependency>
137137
<dependency>

msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/HttpClientIT.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
import org.junit.jupiter.api.Test;
99
import org.junit.jupiter.api.TestInstance;
1010
import org.junit.jupiter.api.BeforeAll;
11-
import static org.junit.jupiter.api.Assertions.assertEquals;
12-
import static org.junit.jupiter.api.Assertions.assertNotNull;
1311

1412
import java.util.Collections;
13+
import java.util.concurrent.ExecutionException;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
import static org.junit.jupiter.api.Assertions.assertNotNull;
17+
import static org.junit.jupiter.api.Assertions.assertThrows;
1518

1619
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
1720
class HttpClientIT {
@@ -34,6 +37,14 @@ void acquireToken_apacheHttpClient() throws Exception {
3437
assertAcquireTokenCommon(user, new ApacheHttpClientAdapter());
3538
}
3639

40+
@Test
41+
void acquireToken_readTimeout() throws Exception {
42+
User user = labUserProvider.getDefaultUser();
43+
44+
//Set a 1ms read timeout, which will almost certainly occur before the service can respond
45+
assertAcquireTokenCommon_WithTimeout(user, 1);
46+
}
47+
3748
private void assertAcquireTokenCommon(User user, IHttpClient httpClient)
3849
throws Exception {
3950
PublicClientApplication pca = PublicClientApplication.builder(
@@ -54,4 +65,22 @@ private void assertAcquireTokenCommon(User user, IHttpClient httpClient)
5465
assertNotNull(result.idToken());
5566
assertEquals(user.getUpn(), result.account().username());
5667
}
68+
69+
private void assertAcquireTokenCommon_WithTimeout(User user, int readTimeout)
70+
throws Exception {
71+
PublicClientApplication pca = PublicClientApplication.builder(
72+
user.getAppId()).
73+
authority(TestConstants.ORGANIZATIONS_AUTHORITY).
74+
readTimeoutForDefaultHttpClient(readTimeout).
75+
build();
76+
77+
ExecutionException ex = assertThrows(ExecutionException.class, () -> pca.acquireToken(UserNamePasswordParameters.
78+
builder(Collections.singleton(TestConstants.GRAPH_DEFAULT_SCOPE),
79+
user.getUpn(),
80+
user.getPassword().toCharArray())
81+
.build())
82+
.get());
83+
84+
assertEquals("com.microsoft.aad.msal4j.MsalClientException: java.net.SocketTimeoutException: Read timed out", ex.getMessage());
85+
}
5786
}

msal4j-sdk/src/integrationtest/java/com.microsoft.aad.msal4j/TokenCacheIT.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.junit.jupiter.api.TestInstance;
1010

1111
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
import static org.junit.jupiter.api.Assertions.assertNotNull;
1213

1314
import java.util.Collections;
1415
import java.util.HashMap;
@@ -170,6 +171,31 @@ void twoAccountsInCache_SameUserDifferentTenants_RemoveAccountTest() throws Exce
170171
"/cache_data/remove-account-test-cache.json");
171172
}
172173

174+
@Test
175+
void retrieveAccounts_ADFSOnPrem() throws Exception {
176+
UserQueryParameters query = new UserQueryParameters();
177+
query.parameters.put(UserQueryParameters.FEDERATION_PROVIDER, FederationProvider.ADFS_2019);
178+
query.parameters.put(UserQueryParameters.USER_TYPE, UserType.ON_PREM);
179+
180+
User user = labUserProvider.getLabUser(query);
181+
182+
PublicClientApplication pca = PublicClientApplication.builder(
183+
TestConstants.ADFS_APP_ID).
184+
authority(TestConstants.ADFS_AUTHORITY).
185+
build();
186+
187+
pca.acquireToken(UserNamePasswordParameters.
188+
builder(Collections.singleton(TestConstants.ADFS_SCOPE),
189+
user.getUpn(),
190+
user.getPassword().toCharArray())
191+
.build())
192+
.get();
193+
194+
assertNotNull(pca.getAccounts().join().iterator().next());
195+
assertEquals(pca.getAccounts().join().size(), 1);
196+
}
197+
198+
173199
private static class TokenPersistence implements ITokenCacheAccessAspect {
174200
String data;
175201

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByInteractiveFlowSupplier.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,11 @@ private AuthorizationResult getAuthorizationResultFromHttpListener() {
209209
expirationTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + 1;
210210
}
211211

212-
while (result == null && !interactiveRequest.futureReference().get().isCancelled() &&
213-
TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) < expirationTime) {
212+
while (result == null && !interactiveRequest.futureReference().get().isCancelled()) {
213+
if (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) > expirationTime) {
214+
LOG.warn(String.format("Listener timed out after %S seconds, no authorization code was returned from the server during that time.", timeFromParameters));
215+
break;
216+
}
214217

215218
result = authorizationResultQueue.poll(100, TimeUnit.MILLISECONDS);
216219
}

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AuthenticationErrorCode.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,19 @@ public class AuthenticationErrorCode {
115115
* A JWT parsing failure, indicating the JWT provided to MSAL is of invalid format.
116116
*/
117117
public final static String INVALID_JWT = "invalid_jwt";
118+
118119
/**
119120
* Indicates that a Broker implementation is missing from the device, such as when an app developer
120121
* does not include one of our broker packages as a dependency in their project, or otherwise cannot
121-
* be accessed by MSAL Java*/
122+
* be accessed by MSAL Java
123+
*/
122124
public final static String MISSING_BROKER = "missing_broker";
125+
126+
/**
127+
* Indicates that a timeout occurred during an HTTP call. If this was thrown in relation to a connection timeout error,
128+
* there is likely a network issue preventing the library from reaching a service, such as being blocked by a firewall.
129+
* If this was thrown in relation to a read timeout error, there is likely an issue in the service itself causing a
130+
* slow response, and this may be resolvable by increasing timeouts. For more details, see https://aka.ms/msal4j-http-client
131+
*/
132+
public final static String HTTP_TIMEOUT = "http_timeout";
123133
}

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/DefaultHttpClient.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
package com.microsoft.aad.msal4j;
22

3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
36
import javax.net.ssl.HttpsURLConnection;
47
import javax.net.ssl.SSLSocketFactory;
58
import java.io.DataOutputStream;
69
import java.io.IOException;
710
import java.io.InputStream;
11+
import java.net.ConnectException;
812
import java.net.HttpURLConnection;
913
import java.net.Proxy;
14+
import java.net.SocketTimeoutException;
1015
import java.net.URL;
1116
import java.nio.charset.StandardCharsets;
1217
import java.util.Map;
1318

1419
class DefaultHttpClient implements IHttpClient {
20+
private final static Logger LOG = LoggerFactory.getLogger(DefaultHttpClient.class);
1521

1622
private final Proxy proxy;
1723
private final SSLSocketFactory sslSocketFactory;
18-
public int DEFAULT_CONNECT_TIMEOUT = 10000;
19-
public int DEFAULT_READ_TIMEOUT = 15000;
2024

21-
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
22-
private int readTimeout = DEFAULT_READ_TIMEOUT;
25+
//By default, rely on the timeout behavior of the services requests are sent to
26+
private int connectTimeout = 0;
27+
private int readTimeout = 0;
2328

2429
DefaultHttpClient(Proxy proxy, SSLSocketFactory sslSocketFactory, Integer connectTimeout, Integer readTimeout) {
2530
this.proxy = proxy;
@@ -117,6 +122,14 @@ private HttpResponse readResponseFromConnection(final HttpsURLConnection conn) t
117122
httpResponse.addHeaders(conn.getHeaderFields());
118123
httpResponse.body(inputStreamToString(is));
119124
return httpResponse;
125+
} catch (SocketTimeoutException readException) {
126+
LOG.error("Timeout while waiting for response from service. If custom timeouts were set, increasing them may resolve this issue. See https://aka.ms/msal4j-http-client for more information and solutions.");
127+
128+
throw readException;
129+
} catch (ConnectException timeoutException) {
130+
LOG.error("Exception while connecting to service, there may be network issues preventing MSAL Java from connecting. See https://aka.ms/msal4j-http-client for more information and solutions.");
131+
132+
throw timeoutException;
120133
} finally {
121134
if (is != null) {
122135
is.close();

msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/TokenCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ Set<IAccount> getAccounts(String clientId) {
346346
((Account) rootAccounts.get(accCached.homeAccountId())).tenantProfiles.put(accCached.realm(), profile);
347347
}
348348

349-
if (accCached.homeAccountId().contains(accCached.localAccountId())) {
349+
if (accCached.localAccountId() != null && accCached.homeAccountId().contains(accCached.localAccountId())) {
350350
((Account) rootAccounts.get(accCached.homeAccountId())).username(accCached.username());
351351
}
352352
}

0 commit comments

Comments
 (0)