Skip to content

Commit 7e7ea1c

Browse files
committed
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentication-library-for-java into avdunn/ciam-custom
2 parents 031e209 + 260656e commit 7e7ea1c

34 files changed

+421
-164
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Quick links:
1616
The library supports the following Java environments:
1717
- Java 8 (or higher)
1818

19-
Current version - 1.16.1
19+
Current version - 1.16.2
2020

2121
You can find the changes for each version in the [change log](https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/main/msal4j-sdk/changelog.txt).
2222

@@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti
2828
<dependency>
2929
<groupId>com.microsoft.azure</groupId>
3030
<artifactId>msal4j</artifactId>
31-
<version>1.16.1</version>
31+
<version>1.16.2</version>
3232
</dependency>
3333
```
3434
### Gradle
3535

3636
```gradle
37-
implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.16.1'
37+
implementation group: 'com.microsoft.azure', name: 'com.microsoft.aad.msal4j', version: '1.16.2'
3838
```
3939

4040
## Usage

changelog.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
Version 1.16.2
2+
=============
3+
- Use SHA256 thumbprints in non-ADFS cert flows (#840)
4+
- Reduce logging level of cache miss messages (#844)
5+
- Make ManagedIdentitySourceType enum public (#845)
6+
17
Version 1.16.1
28
=============
39
- Add missing refreshOn metadata (#838)

msal4j-persistence-extension/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<dependency>
3939
<groupId>com.microsoft.azure</groupId>
4040
<artifactId>msal4j</artifactId>
41-
<version>1.15.0</version>
41+
<version>1.15.1</version>
4242
</dependency>
4343

4444
<dependency>

msal4j-sdk/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Quick links:
1616
The library supports the following Java environments:
1717
- Java 8 (or higher)
1818

19-
Current version - 1.16.1
19+
Current version - 1.16.2
2020

2121
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).
2222

@@ -28,13 +28,13 @@ Find [the latest package in the Maven repository](https://mvnrepository.com/arti
2828
<dependency>
2929
<groupId>com.microsoft.azure</groupId>
3030
<artifactId>msal4j</artifactId>
31-
<version>1.16.1</version>
31+
<version>1.16.2</version>
3232
</dependency>
3333
```
3434
### Gradle
3535

3636
```gradle
37-
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.16.1'
37+
compile group: 'com.microsoft.azure', name: 'msal4j', version: '1.16.2'
3838
```
3939

4040
## Usage

msal4j-sdk/bnd.bnd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Export-Package: com.microsoft.aad.msal4j;version="1.16.1"
1+
Export-Package: com.microsoft.aad.msal4j;version="1.16.2"
22
Automatic-Module-Name: com.microsoft.aad.msal4j

msal4j-sdk/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>com.microsoft.azure</groupId>
55
<artifactId>msal4j</artifactId>
6-
<version>1.16.1</version>
6+
<version>1.16.2</version>
77
<packaging>jar</packaging>
88
<name>msal4j</name>
99
<description>

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ void acquireTokenSilent_ConfidentialClient_acquireTokenSilent(String environment
193193
}
194194

195195
@Test
196-
public void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrowsException()
196+
void acquireTokenSilent_ConfidentialClient_acquireTokenSilentDifferentScopeThrowsException()
197197
throws Exception {
198198
cfg = new Config(AzureEnvironment.AZURE);
199199

@@ -344,6 +344,48 @@ void acquireTokenSilent_emptyScopeSet(String environment) throws Exception {
344344
assertEquals(result.accessToken(), silentResult.accessToken());
345345
}
346346

347+
@Test
348+
public void acquireTokenSilent_ClaimsForceRefresh() throws Exception {
349+
cfg = new Config(AzureEnvironment.AZURE);
350+
User user = labUserProvider.getDefaultUser(AzureEnvironment.AZURE);
351+
352+
Set<String> scopes = new HashSet<>();
353+
PublicClientApplication pca = PublicClientApplication.builder(
354+
user.getAppId()).
355+
authority(cfg.organizationsAuthority()).
356+
build();
357+
358+
IAuthenticationResult result = pca.acquireToken(UserNamePasswordParameters.
359+
builder(scopes,
360+
user.getUpn(),
361+
user.getPassword().toCharArray())
362+
.build())
363+
.get();
364+
365+
assertResultNotNull(result);
366+
367+
IAuthenticationResult silentResultWithoutClaims = pca.acquireTokenSilently(SilentParameters.
368+
builder(scopes, result.account())
369+
.build())
370+
.get();
371+
372+
assertResultNotNull(silentResultWithoutClaims);
373+
assertEquals(result.accessToken(), silentResultWithoutClaims.accessToken());
374+
375+
//If claims are added to a silent request, it should trigger the refresh flow and return a new token
376+
ClaimsRequest cr = new ClaimsRequest();
377+
cr.requestClaimInAccessToken("email", null);
378+
379+
IAuthenticationResult silentResultWithClaims = pca.acquireTokenSilently(SilentParameters.
380+
builder(scopes, result.account())
381+
.claims(cr)
382+
.build())
383+
.get();
384+
385+
assertResultNotNull(silentResultWithClaims);
386+
assertNotEquals(result.accessToken(), silentResultWithClaims.accessToken());
387+
}
388+
347389
private IConfidentialClientApplication getConfidentialClientApplications() throws Exception {
348390
String clientId = cfg.appProvider.getOboAppId();
349391
String password = cfg.appProvider.getOboAppPassword();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ private ClientAssertion getClientAssertion(String clientId) {
179179
clientId,
180180
(ClientCertificate) certificate,
181181
"https://login.microsoftonline.com/common/oauth2/v2.0/token",
182-
true);
182+
true, false);
183183
}
184184

185185
private void assertAcquireTokenCommon(String clientId, IClientCredential credential, String authority) throws Exception {

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
//base class for all sources that support managed identity
1616
abstract class AbstractManagedIdentitySource {
1717

18-
protected static final String TIMEOUT_ERROR = "[Managed Identity] Authentication unavailable. The request to the managed identity endpoint timed out.";
1918
private static final Logger LOG = LoggerFactory.getLogger(AbstractManagedIdentitySource.class);
2019
private static final String MANAGED_IDENTITY_NO_RESPONSE_RECEIVED = "[Managed Identity] Authentication unavailable. No response received from the managed identity endpoint.";
2120

2221
protected final ManagedIdentityRequest managedIdentityRequest;
2322
protected final ServiceBundle serviceBundle;
2423
ManagedIdentitySourceType managedIdentitySourceType;
24+
ManagedIdentityIdType idType;
25+
String userAssignedId;
2526

2627
@Getter
2728
@Setter
@@ -40,6 +41,8 @@ public AbstractManagedIdentitySource(MsalRequest msalRequest, ServiceBundle serv
4041
this.managedIdentityRequest = (ManagedIdentityRequest) msalRequest;
4142
this.managedIdentitySourceType = sourceType;
4243
this.serviceBundle = serviceBundle;
44+
this.idType = ((ManagedIdentityApplication) msalRequest.application()).getManagedIdentityId().getIdType();
45+
this.userAssignedId = ((ManagedIdentityApplication) msalRequest.application()).getManagedIdentityId().getUserAssignedId();
4346
}
4447

4548
public ManagedIdentityResponse getManagedIdentityResponse(
@@ -49,15 +52,9 @@ public ManagedIdentityResponse getManagedIdentityResponse(
4952
IHttpResponse response;
5053

5154
try {
52-
53-
HttpRequest httpRequest = managedIdentityRequest.method.equals(HttpMethod.GET) ?
54-
new HttpRequest(HttpMethod.GET,
55-
managedIdentityRequest.computeURI().toString(),
56-
managedIdentityRequest.headers) :
57-
new HttpRequest(HttpMethod.POST,
55+
HttpRequest httpRequest = new HttpRequest(managedIdentityRequest.method,
5856
managedIdentityRequest.computeURI().toString(),
59-
managedIdentityRequest.headers,
60-
managedIdentityRequest.getBodyAsString());
57+
managedIdentityRequest.headers);
6158
response = serviceBundle.getHttpHelper().executeHttpRequest(httpRequest, managedIdentityRequest.requestContext(), serviceBundle);
6259
} catch (URISyntaxException e) {
6360
throw new RuntimeException(e);

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

Lines changed: 82 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
class AcquireTokenSilentSupplier extends AuthenticationResultSupplier {
1414

1515
private SilentRequest silentRequest;
16+
protected static final int ACCESS_TOKEN_EXPIRE_BUFFER_IN_SEC = 5 * 60;
1617

1718
AcquireTokenSilentSupplier(AbstractApplicationBase clientApplication, SilentRequest silentRequest) {
1819
super(clientApplication, silentRequest);
@@ -22,6 +23,7 @@ class AcquireTokenSilentSupplier extends AuthenticationResultSupplier {
2223

2324
@Override
2425
AuthenticationResult execute() throws Exception {
26+
boolean shouldRefresh;
2527
Authority requestAuthority = silentRequest.requestAuthority();
2628
if (requestAuthority.authorityType != AuthorityType.B2C) {
2729
requestAuthority =
@@ -53,29 +55,9 @@ AuthenticationResult execute() throws Exception {
5355
clientApplication.serviceBundle().getServerSideTelemetry().incrementSilentSuccessfulCount();
5456
}
5557

56-
//Determine if the current token needs to be refreshed according to the refresh_in value
57-
long currTimeStampSec = new Date().getTime() / 1000;
58-
boolean afterRefreshOn = res.refreshOn() != null && res.refreshOn() > 0 &&
59-
res.refreshOn() < currTimeStampSec && res.expiresOn() >= currTimeStampSec;
60-
61-
if (silentRequest.parameters().forceRefresh() || afterRefreshOn || StringHelper.isBlank(res.accessToken())) {
62-
63-
//As of version 3 of the telemetry schema, there is a field for collecting data about why a token was refreshed,
64-
// so here we set the telemetry value based on the cause of the refresh
65-
if (silentRequest.parameters().forceRefresh()) {
66-
clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo(
67-
CacheTelemetry.REFRESH_FORCE_REFRESH.telemetryValue);
68-
} else if (afterRefreshOn) {
69-
clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo(
70-
CacheTelemetry.REFRESH_REFRESH_IN.telemetryValue);
71-
} else if (res.expiresOn() < currTimeStampSec) {
72-
clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo(
73-
CacheTelemetry.REFRESH_ACCESS_TOKEN_EXPIRED.telemetryValue);
74-
} else if (StringHelper.isBlank(res.accessToken())) {
75-
clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo(
76-
CacheTelemetry.REFRESH_NO_ACCESS_TOKEN.telemetryValue);
77-
}
58+
shouldRefresh = shouldRefresh(silentRequest.parameters(), res);
7859

60+
if (shouldRefresh || clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo() == CacheTelemetry.REFRESH_REFRESH_IN.telemetryValue) {
7961
if (!StringHelper.isBlank(res.refreshToken())) {
8062
//There are certain scenarios where the cached authority may differ from the client app's authority,
8163
// such as when a request is instance aware. Unless overridden by SilentParameters.authorityUrl, the
@@ -84,29 +66,7 @@ AuthenticationResult execute() throws Exception {
8466
requestAuthority = Authority.createAuthority(new URL(requestAuthority.authority().replace(requestAuthority.host(),
8567
res.account().environment())));
8668
}
87-
88-
RefreshTokenRequest refreshTokenRequest = new RefreshTokenRequest(
89-
RefreshTokenParameters.builder(silentRequest.parameters().scopes(), res.refreshToken()).build(),
90-
silentRequest.application(),
91-
silentRequest.requestContext(),
92-
silentRequest);
93-
94-
AcquireTokenByAuthorizationGrantSupplier acquireTokenByAuthorisationGrantSupplier =
95-
new AcquireTokenByAuthorizationGrantSupplier(clientApplication, refreshTokenRequest, requestAuthority);
96-
97-
try {
98-
res = acquireTokenByAuthorisationGrantSupplier.execute();
99-
100-
res.metadata().tokenSource(TokenSource.IDENTITY_PROVIDER);
101-
102-
log.info("Access token refreshed successfully.");
103-
} catch (MsalServiceException ex) {
104-
//If the token refresh attempt threw a MsalServiceException but the refresh attempt was done
105-
// only because of refreshOn, then simply return the existing cached token
106-
if (afterRefreshOn && !(silentRequest.parameters().forceRefresh() || StringHelper.isBlank(res.accessToken()))) {
107-
return res;
108-
} else throw ex;
109-
}
69+
res = makeRefreshRequest(res, requestAuthority);
11070
} else {
11171
res = null;
11272
}
@@ -120,4 +80,81 @@ AuthenticationResult execute() throws Exception {
12080

12181
return res;
12282
}
83+
84+
private AuthenticationResult makeRefreshRequest(AuthenticationResult cachedResult, Authority requestAuthority) throws Exception {
85+
RefreshTokenRequest refreshTokenRequest = new RefreshTokenRequest(
86+
RefreshTokenParameters.builder(silentRequest.parameters().scopes(), cachedResult.refreshToken()).build(),
87+
silentRequest.application(),
88+
silentRequest.requestContext(),
89+
silentRequest);
90+
91+
AcquireTokenByAuthorizationGrantSupplier acquireTokenByAuthorisationGrantSupplier =
92+
new AcquireTokenByAuthorizationGrantSupplier(clientApplication, refreshTokenRequest, requestAuthority);
93+
94+
try {
95+
AuthenticationResult refreshedResult = acquireTokenByAuthorisationGrantSupplier.execute();
96+
97+
refreshedResult.metadata().tokenSource(TokenSource.IDENTITY_PROVIDER);
98+
99+
log.info("Access token refreshed successfully.");
100+
return refreshedResult;
101+
} catch (MsalServiceException ex) {
102+
//If the token refresh attempt threw a MsalServiceException but the refresh attempt was done
103+
// only because of refreshOn, then simply return the existing cached token rather than throw an exception
104+
if (clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo() == CacheTelemetry.REFRESH_REFRESH_IN.telemetryValue) {
105+
return cachedResult;
106+
}
107+
throw ex;
108+
}
109+
}
110+
111+
//Handles any logic to determine if a token should be refreshed, based on the request parameters and the status of cached tokens
112+
private boolean shouldRefresh(SilentParameters parameters, AuthenticationResult cachedResult) {
113+
114+
//If forceRefresh is true, no reason to check any other option
115+
if (parameters.forceRefresh()) {
116+
setCacheTelemetry(CacheTelemetry.REFRESH_FORCE_REFRESH.telemetryValue);
117+
log.debug("Refreshing access token because forceRefresh parameter is true.");
118+
return true;
119+
}
120+
121+
//If the request contains claims then the token should be refreshed, to ensure that the returned token has the correct claims
122+
// Note: these are the types of claims found in (for example) a claims challenge, and do not include client capabilities
123+
if (parameters.claims() != null) {
124+
setCacheTelemetry(CacheTelemetry.REFRESH_FORCE_REFRESH.telemetryValue);
125+
log.debug("Refreshing access token because the claims parameter is not null.");
126+
return true;
127+
}
128+
129+
long currTimeStampSec = new Date().getTime() / 1000;
130+
131+
//If the access token is expired or within 5 minutes of becoming expired, refresh it
132+
if (!StringHelper.isBlank(cachedResult.accessToken()) && cachedResult.expiresOn() < (currTimeStampSec - ACCESS_TOKEN_EXPIRE_BUFFER_IN_SEC)) {
133+
setCacheTelemetry(CacheTelemetry.REFRESH_ACCESS_TOKEN_EXPIRED.telemetryValue);
134+
log.debug("Refreshing access token because it is expired.");
135+
return true;
136+
}
137+
138+
//Certain long-lived tokens will have a 'refresh on' time that indicates a refresh should be attempted long before the token would expire
139+
if (!StringHelper.isBlank(cachedResult.accessToken()) &&
140+
cachedResult.refreshOn() != null && cachedResult.refreshOn() > 0 &&
141+
cachedResult.refreshOn() < currTimeStampSec && cachedResult.expiresOn() >= (currTimeStampSec + ACCESS_TOKEN_EXPIRE_BUFFER_IN_SEC)){
142+
setCacheTelemetry(CacheTelemetry.REFRESH_REFRESH_IN.telemetryValue);
143+
log.debug("Attempting to refresh access token because it is after the refreshOn time.");
144+
return true;
145+
}
146+
147+
//If there is a refresh token but no access token, we should use the refresh token to get the access token
148+
if (StringHelper.isBlank(cachedResult.accessToken()) && !StringHelper.isBlank(cachedResult.refreshToken())) {
149+
setCacheTelemetry(CacheTelemetry.REFRESH_NO_ACCESS_TOKEN.telemetryValue);
150+
log.debug("Refreshing access token because it was missing from the cache.");
151+
return true;
152+
}
153+
154+
return false;
155+
}
156+
157+
private void setCacheTelemetry(int cacheInfoValue){
158+
clientApplication.serviceBundle().getServerSideTelemetry().getCurrentRequest().cacheInfo(cacheInfoValue);
159+
}
123160
}

0 commit comments

Comments
 (0)