Skip to content

Commit 956bcb1

Browse files
committed
Merge branch 'dev' into avdunn/merge-conflicts
# Conflicts: # README.md # changelog.txt # msal4j-sdk/README.md # msal4j-sdk/bnd.bnd # msal4j-sdk/pom.xml # msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/HttpHelper.java # msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ManagedIdentityRequest.java # msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/StringHelper.java # msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/TokenRequestExecutor.java # msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/CacheFormatTests.java # msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/ClientCertificateTest.java # msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/ManagedIdentityTests.java # msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/TokenRequestExecutorTest.java
2 parents 4699522 + 4e7c182 commit 956bcb1

File tree

63 files changed

+1884
-852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1884
-852
lines changed

RELEASES.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Microsoft Identity SDK Versioning and Servicing FAQ
2+
3+
We have adopted the semantic versioning flow that is industry standard for OSS projects. It gives the maximum amount of control on what risk you take with what versions.
4+
5+
## Semantic Versioning and API stability promises
6+
7+
Microsoft Authentication Libraries are independent open source libraries that are used by both internal and external Microsoft partners. As with the rest of Microsoft, we have moved to a rapid iteration model where bugs are fixed daily and new versions are produced as required. To communicate these frequent changes to external partners and customers, we follow the practices of other open source libraries and use semantic versioning for all our public Microsoft Authentication SDK libraries. This allows us to support our downstream partners which will lock on certain versions for stability purposes, as well as providing for the distribution over NuGet, CocoaPods, and Maven.
8+
9+
The semantics are: MAJOR.MINOR.PATCH (example 1.1.5)
10+
11+
We will update our code distributions to use the latest PATCH semantic version number in order to make sure our customers and partners get the latest bug fixes. Downstream partner needs to pull the latest PATCH version. Most partners should try lock on the latest MINOR version number in their builds and accept any updates in the PATCH number.
12+
13+
Example:
14+
Using Maven, this ensures all 1.1.0 to 1.1.x updates are included when building your code, but not 1.2.
15+
16+
```
17+
<dependency>
18+
<groupId>com.microsoft.azure</groupId>
19+
<artifactId>msal4j</artifactId>
20+
<version>[1.1.0,1.2.0)</version>
21+
</dependency>
22+
```
23+
24+
| Version | Description |
25+
|:-------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
26+
| x.x.x | PATCH version number. Incrementing these numbers is for bug fixes and updates but do not introduce new features. This is used for close partners who build on our platform release (ex. Microsoft Entra Fabric, Office, etc.). In addition, Cocoapods, NuGet, and Maven use this number to deliver the latest release to customers. This will update frequently (sometimes within the same day). There are no new features, and no regressions or API surface changes. Code will continue to work unless affected by a particular code fix. |
27+
| x.x | MINOR version numbers. These are for new feature additions that do not impact existing features or introduce regressions. They are purely additive, but may require testing to ensure nothing is impacted. All x.x.x bug fixes will also roll up in to this number. There is no regressions or API surface changes. Code will continue to work unless affected by a particular code fix or needs this new feature. |
28+
| x | MAJOR version numbers. This should be considered a new, supported version of Microsoft Authentication SDK and begins the Azure one year support cycle anew. Major new features are introduced and API changes can occur. This should only be used after a large amount of testing and used only if those features are needed. We will continue to service MAJOR version numbers with bug fixes up to the one year support cycle. |
29+
30+
## Serviceability
31+
32+
When we release a new MINOR version, the previous MINOR versions shipped within one year MAY still accept bug report and receive patches. MINOR versions more than one year old will not receive any support. If you suspect there is an issue in a 1+ year old version that you are using, please upgrade to latest MINOR version and retry, before you send out a bug report.
33+
34+
When we release a new MAJOR version, we will continue to apply bug fixes to the existing features in the previous MAJOR version for up to the 1-year support cycle for Azure.
35+
36+
## Microsoft Authentication SDKs and Microsoft Entra
37+
38+
Microsoft Authentication SDKs major versions will maintain backwards compatibility with Microsoft Entra web services through the support period. This means that the API surface area defined in a MAJOR version will continue to work for at least 1 year after release.
39+
40+
We will respond to bugs quickly from our partners and customers submitted through GitHub and through our private alias ([email protected]) for security issues and update the PATCH version number. We will also submit a change summary for each PATCH number.
41+
Occasionally, there will be security bugs or breaking bugs from our partners that will require an immediate fix and an update to all partners and customers. When this occurs, we will do an emergency roll up to a PATCH version number and update all our distribution methods to the latest.

changelog.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ Version 1.30.0-beta
44
- Replace com.nimbusds dependencies with implementations of OAuth behavior (#926, #927, #928, #941, #945)
55
- Replace com.fasterxml.jackson with com.azure.json for JSON behavior (#947, #948)
66

7+
Version 1.22.0
8+
=============
9+
- Validate issuer from OIDC endpoint when using the oidcAuthority() API (#970)
10+
- Bump oauth2-oidc-sdk dependency to avoid CVE-2025-53864 (#975)
11+
12+
Version 1.21.0
13+
=============
14+
- Add support for claims, client capabilities, and token revocation in Service Fabric scenarios (#929, #943)
15+
- Improve retry logic for HTTP requests, and add API to disable retries (#960, #963, #964)
16+
- Support multiple date formats in Managed identity scenarios (#956)
17+
- Fix query parameter issue in IMDS scenarios (#954)
18+
- Update dependencies used in tests to avoid CVE warnings (#962)
19+
720
1.20.1
821
=============
922
- Fix Base64URL decoding bug (#938)

msal4j-sdk/pom.xml

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,11 @@
3030

3131
<properties>
3232
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
33+
<skip.unit.tests>false</skip.unit.tests>
34+
<skip.integration.tests>false</skip.integration.tests>
3335
</properties>
3436

3537
<dependencies>
36-
<dependency>
37-
<groupId>org.slf4j</groupId>
38-
<artifactId>slf4j-api</artifactId>
39-
<version>1.7.36</version>
40-
</dependency>
4138
<dependency>
4239
<groupId>com.azure</groupId>
4340
<artifactId>azure-json</artifactId>
@@ -66,31 +63,31 @@
6663
<dependency>
6764
<groupId>org.junit.jupiter</groupId>
6865
<artifactId>junit-jupiter-api</artifactId>
69-
<version>5.9.2</version>
66+
<version>5.13.0</version>
7067
<scope>test</scope>
7168
</dependency>
7269
<dependency>
7370
<groupId>org.junit.jupiter</groupId>
7471
<artifactId>junit-jupiter-params</artifactId>
75-
<version>5.8.1</version>
72+
<version>5.13.0</version>
7673
<scope>test</scope>
7774
</dependency>
7875
<dependency>
7976
<groupId>org.junit.jupiter</groupId>
8077
<artifactId>junit-jupiter-engine</artifactId>
81-
<version>5.9.2</version>
78+
<version>5.13.0</version>
8279
<scope>test</scope>
8380
</dependency>
8481
<dependency>
8582
<groupId>org.mockito</groupId>
8683
<artifactId>mockito-inline</artifactId>
87-
<version>4.7.0</version>
84+
<version>4.11.0</version>
8885
<scope>test</scope>
8986
</dependency>
9087
<dependency>
9188
<groupId>org.mockito</groupId>
9289
<artifactId>mockito-junit-jupiter</artifactId>
93-
<version>4.7.0</version>
90+
<version>4.11.0</version>
9491
<scope>test</scope>
9592
</dependency>
9693
<dependency>
@@ -102,19 +99,13 @@
10299
<dependency>
103100
<groupId>org.skyscreamer</groupId>
104101
<artifactId>jsonassert</artifactId>
105-
<version>1.5.0</version>
106-
<scope>test</scope>
107-
</dependency>
108-
<dependency>
109-
<groupId>org.apache.httpcomponents</groupId>
110-
<artifactId>httpclient</artifactId>
111-
<version>4.5.13</version>
102+
<version>1.5.3</version>
112103
<scope>test</scope>
113104
</dependency>
114105
<dependency>
115106
<groupId>com.azure</groupId>
116107
<artifactId>azure-security-keyvault-secrets</artifactId>
117-
<version>4.3.5</version>
108+
<version>4.9.4</version>
118109
<scope>test</scope>
119110
</dependency>
120111
<dependency>
@@ -124,15 +115,9 @@
124115
<scope>test</scope>
125116
</dependency>
126117
<dependency>
127-
<groupId>com.google.guava</groupId>
128-
<artifactId>guava</artifactId>
129-
<version>32.1.1-jre</version>
130-
<scope>test</scope>
131-
</dependency>
132-
<dependency>
133-
<groupId>ch.qos.logback</groupId>
134-
<artifactId>logback-classic</artifactId>
135-
<version>1.3.12</version>
118+
<groupId>org.apache.commons</groupId>
119+
<artifactId>commons-text</artifactId>
120+
<version>1.13.1</version>
136121
<scope>test</scope>
137122
</dependency>
138123
<dependency>
@@ -189,7 +174,9 @@
189174
<executions>
190175
<execution>
191176
<id>check</id>
192-
<goals><goal>check</goal></goals>
177+
<goals>
178+
<goal>check</goal>
179+
</goals>
193180
</execution>
194181
</executions>
195182
</plugin>
@@ -214,9 +201,9 @@
214201
<version>3.5.2</version>
215202
<configuration>
216203
<argLine>@{argLine} -noverify</argLine>
204+
<skipTests>${skip.unit.tests}</skipTests>
217205
</configuration>
218206
</plugin>
219-
220207
<plugin>
221208
<groupId>org.apache.maven.plugins</groupId>
222209
<artifactId>maven-javadoc-plugin</artifactId>
@@ -291,6 +278,9 @@
291278
</goals>
292279
</execution>
293280
</executions>
281+
<configuration>
282+
<skipTests>${skip.integration.tests}</skipTests>
283+
</configuration>
294284
</plugin>
295285
<plugin>
296286
<groupId>biz.aQute.bnd</groupId>
@@ -323,6 +313,10 @@
323313
</execution>
324314
</executions>
325315
</plugin>
316+
<plugin>
317+
<artifactId>maven-dependency-plugin</artifactId>
318+
<version>3.1.2</version>
319+
</plugin>
326320
</plugins>
327321
</build>
328322
</project>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ static AadInstanceDiscoveryResponse sendInstanceDiscoveryRequest(URL authorityUr
235235

236236
AadInstanceDiscoveryResponse response = JsonHelper.convertJsonStringToJsonSerializableObject(httpResponse.body(), AadInstanceDiscoveryResponse::fromJson);
237237

238-
if (httpResponse.statusCode() != HttpHelper.HTTP_STATUS_200) {
239-
if (httpResponse.statusCode() == HttpHelper.HTTP_STATUS_400 && response.error().equals("invalid_instance")) {
238+
if (httpResponse.statusCode() != HttpStatus.HTTP_OK) {
239+
if (httpResponse.statusCode() == HttpStatus.HTTP_BAD_REQUEST && response.error().equals("invalid_instance")) {
240240
// instance discovery failed due to an invalid authority, throw an exception.
241241
throw MsalServiceExceptionFactory.fromHttpResponse(httpResponse);
242242
}
@@ -310,7 +310,7 @@ static String discoverRegion(MsalRequest msalRequest, ServiceBundle serviceBundl
310310
log.info("Starting call to IMDS endpoint.");
311311
IHttpResponse httpResponse = future.get(IMDS_TIMEOUT, IMDS_TIMEOUT_UNIT);
312312
//If call to IMDS endpoint was successful, return region from response body
313-
if (httpResponse.statusCode() == HttpHelper.HTTP_STATUS_200 && !httpResponse.body().isEmpty()) {
313+
if (httpResponse.statusCode() == HttpStatus.HTTP_OK && !httpResponse.body().isEmpty()) {
314314
log.info(String.format("Region retrieved from IMDS endpoint: %s", httpResponse.body()));
315315
currentRequest.regionSource(RegionTelemetry.REGION_SOURCE_IMDS.telemetryValue);
316316

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public abstract class AbstractApplicationBase implements IApplicationBase {
3232
private IHttpClient httpClient;
3333
private Integer connectTimeoutForDefaultHttpClient;
3434
private Integer readTimeoutForDefaultHttpClient;
35+
private boolean retryDisabled;
3536
String tenant;
3637

3738
//The following fields are set in only some applications and/or set internally by the library. To avoid excessive
@@ -150,6 +151,10 @@ public Integer readTimeoutForDefaultHttpClient() {
150151
return this.readTimeoutForDefaultHttpClient;
151152
}
152153

154+
boolean isRetryDisabled() {
155+
return this.retryDisabled;
156+
}
157+
153158
String tenant() {
154159
return this.tenant;
155160
}
@@ -190,6 +195,7 @@ public abstract static class Builder<T extends Builder<T>> {
190195
Boolean onlySendFailureTelemetry = false;
191196
Integer connectTimeoutForDefaultHttpClient;
192197
Integer readTimeoutForDefaultHttpClient;
198+
boolean disableInternalRetries;
193199
private String clientId;
194200
private Authority authenticationAuthority = createDefaultAADAuthority();
195201

@@ -319,6 +325,18 @@ public T readTimeoutForDefaultHttpClient(Integer val) {
319325
return self();
320326
}
321327

328+
/**
329+
* The library has a number of policies for retrying HTTP calls in different scenarios.
330+
* <p>
331+
* This will disable all internal retry behavior, allowing customized retry behavior via your own implementation of {@link IHttpClient}
332+
*
333+
* @return instance of the Builder on which method was called
334+
*/
335+
public T disableInternalRetries() {
336+
disableInternalRetries = true;
337+
return self();
338+
}
339+
322340
T telemetryConsumer(Consumer<List<HashMap<String, String>>> val) {
323341
validateNotNull("telemetryConsumer", val);
324342

@@ -356,5 +374,16 @@ private static Authority createDefaultAADAuthority() {
356374
readTimeoutForDefaultHttpClient = builder.readTimeoutForDefaultHttpClient;
357375
authenticationAuthority = builder.authenticationAuthority;
358376
clientId = builder.clientId;
377+
retryDisabled = builder.disableInternalRetries;
378+
379+
if (builder.httpClient == null) {
380+
httpClient = new DefaultHttpClient(
381+
builder.proxy,
382+
builder.sslSocketFactory,
383+
builder.connectTimeoutForDefaultHttpClient,
384+
builder.readTimeoutForDefaultHttpClient);
385+
} else {
386+
httpClient = builder.httpClient;
387+
}
359388
}
360389
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,7 @@ public T correlationId(String val) {
562562
super.serviceBundle = new ServiceBundle(
563563
builder.executorService,
564564
new TelemetryManager(telemetryConsumer, builder.onlySendFailureTelemetry),
565-
new HttpHelper(builder.httpClient == null ?
566-
new DefaultHttpClient(builder.proxy, builder.sslSocketFactory, builder.connectTimeoutForDefaultHttpClient, builder.readTimeoutForDefaultHttpClient) :
567-
builder.httpClient)
565+
new HttpHelper(this, new DefaultRetryPolicy())
568566
);
569567

570568
if (aadAadInstanceDiscoveryResponse != null) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public ManagedIdentityResponse getManagedIdentityResponse(
4141
ManagedIdentityParameters parameters) {
4242

4343
createManagedIdentityRequest(parameters.resource);
44+
managedIdentityRequest.addTokenRevocationParametersToQuery(parameters);
4445
IHttpResponse response;
4546

4647
try {

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
88

9+
import java.time.Instant;
910
import java.util.HashSet;
1011
import java.util.Set;
1112

@@ -83,6 +84,12 @@ AuthenticationResult execute() throws Exception {
8384
result.metadata().tokenSource(TokenSource.CACHE);
8485
return result;
8586
} else {
87+
if (cacheRefreshReason == CacheRefreshReason.CLAIMS) {
88+
LOG.debug("Claims are passed, creating token hash and refreshing the token");
89+
managedIdentityParameters.revokedTokenHash = StringHelper.createSha256HashHexString(result.accessToken());
90+
return fetchNewAccessTokenAndSaveToCache(tokenRequestExecutor, CacheRefreshReason.CLAIMS);
91+
}
92+
8693
LOG.debug(String.format("Refreshing access token. Cache refresh reason: %s", cacheRefreshReason));
8794
return fetchNewAccessTokenAndSaveToCache(tokenRequestExecutor, cacheRefreshReason);
8895
}
@@ -109,14 +116,13 @@ private AuthenticationResult fetchNewAccessTokenAndSaveToCache(TokenRequestExecu
109116

110117
AuthenticationResult authenticationResult = createFromManagedIdentityResponse(managedIdentityResponse);
111118
clientApplication.tokenCache.saveTokens(tokenRequestExecutor, authenticationResult, clientApplication.authenticationAuthority.host);
112-
AuthenticationResult result = authenticationResult;
113-
result.metadata().tokenSource(TokenSource.IDENTITY_PROVIDER);
114-
result.metadata().cacheRefreshReason(cacheRefreshReason);
115-
return result;
119+
authenticationResult.metadata().tokenSource(TokenSource.IDENTITY_PROVIDER);
120+
authenticationResult.metadata().cacheRefreshReason(cacheRefreshReason);
121+
return authenticationResult;
116122
}
117123

118124
private AuthenticationResult createFromManagedIdentityResponse(ManagedIdentityResponse managedIdentityResponse) {
119-
long expiresOn = Long.parseLong(managedIdentityResponse.expiresOn);
125+
long expiresOn = getExpiresOnFromManagedIdentityTimestamp(managedIdentityResponse.expiresOn);
120126
long refreshOn = calculateRefreshOn(expiresOn);
121127
AuthenticationResultMetadata metadata = AuthenticationResultMetadata.builder()
122128
.tokenSource(TokenSource.IDENTITY_PROVIDER)
@@ -133,6 +139,31 @@ private AuthenticationResult createFromManagedIdentityResponse(ManagedIdentityRe
133139
.build();
134140
}
135141

142+
static long getExpiresOnFromManagedIdentityTimestamp(String dateTimeStamp) {
143+
if (dateTimeStamp == null || dateTimeStamp.isEmpty()) {
144+
return 0;
145+
}
146+
147+
// Try parsing as Unix timestamp (seconds since epoch)
148+
try {
149+
return Long.parseLong(dateTimeStamp);
150+
} catch (NumberFormatException e) {
151+
// Not a number
152+
}
153+
154+
// Try parsing as ISO 8601
155+
try {
156+
return Instant.parse(dateTimeStamp).getEpochSecond();
157+
} catch (Exception e) {
158+
// Not ISO 8601
159+
}
160+
161+
throw new MsalClientException(
162+
String.format("Failed to parse timestamp '%s'. Expected Unix epoch seconds or ISO 8601 format.",
163+
dateTimeStamp),
164+
AuthenticationErrorCode.INVALID_TIMESTAMP_FORMAT);
165+
}
166+
136167
private long calculateRefreshOn(long expiresOn) {
137168
long timestampSeconds = System.currentTimeMillis() / 1000;
138169
long expiresIn = expiresOn - timestampSeconds;

0 commit comments

Comments
 (0)