Skip to content

Commit db89fb8

Browse files
committed
AI generated code with spec and prompt shared
1 parent 54f3d44 commit db89fb8

8 files changed

Lines changed: 192 additions & 4 deletions

File tree

msal4j-sdk/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@
157157
<version>2.14.0</version>
158158
<scope>test</scope>
159159
</dependency>
160+
<dependency>
161+
<groupId>jakarta.xml.bind</groupId>
162+
<artifactId>jakarta.xml.bind-api</artifactId>
163+
<version>2.3.2</version>
164+
<scope>compile</scope>
165+
</dependency>
160166
</dependencies>
161167

162168
<!-- force https -->

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,10 @@ public class AuthenticationErrorCode {
147147
* For more information on managed identity see https://aka.ms/msal4j-managed-identity.
148148
*/
149149
public static final String MANAGED_IDENTITY_REQUEST_FAILED = "managed_identity_request_failed";
150+
151+
/**
152+
* Indicates a cryptographic operation error occurred, such as when generating hash values
153+
* or performing other cryptographic functions.
154+
*/
155+
public static final String CRYPTO_ERROR = "crypto_error";
150156
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,15 @@ final class Constants {
2323
public static final String MSI_ENDPOINT = "MSI_ENDPOINT";
2424
public static final String IDENTITY_SERVER_THUMBPRINT = "IDENTITY_SERVER_THUMBPRINT";
2525

26+
// Constants for token revocation and client capabilities
27+
public static final String CLIENT_CAPABILITY_CP1 = "cp1";
28+
public static final String TOKEN_REVOCATION_REQUEST_PARAM = "x-ms-revoke-token";
29+
public static final String TOKEN_HASH_CLAIM = "x-ms-token-hash";
30+
31+
// Only Service Fabric and App Service managed identity environments support token revocation
32+
public static final ManagedIdentitySourceType[] TOKEN_REVOCATION_SUPPORTED_ENVIRONMENTS = {
33+
ManagedIdentitySourceType.APP_SERVICE,
34+
ManagedIdentitySourceType.SERVICE_FABRIC
35+
};
36+
2637
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
public class ManagedIdentityApplication extends AbstractApplicationBase implements IManagedIdentityApplication {
1818

1919
private final ManagedIdentityId managedIdentityId;
20+
private List<String> clientCapabilities;
2021
static TokenCache sharedTokenCache = new TokenCache();
2122

2223
//Deprecated the field in favor of the static getManagedIdentitySource method
@@ -44,6 +45,7 @@ private ManagedIdentityApplication(Builder builder) {
4445

4546
this.managedIdentityId = builder.managedIdentityId;
4647
this.tenant = Constants.MANAGED_IDENTITY_DEFAULT_TENTANT;
48+
this.clientCapabilities = builder.clientCapabilities;
4749
}
4850

4951
public static TokenCache getSharedTokenCache() {
@@ -57,6 +59,8 @@ static IEnvironmentVariables getEnvironmentVariables() {
5759
public ManagedIdentityId getManagedIdentityId() {
5860
return this.managedIdentityId;
5961
}
62+
63+
public List<String> getClientCapabilities() { return this.clientCapabilities; }
6064

6165
@Override
6266
public CompletableFuture<IAuthenticationResult> acquireTokenForManagedIdentity(ManagedIdentityParameters managedIdentityParameters)
@@ -105,7 +109,7 @@ public Builder resource(String resource) {
105109
/**
106110
* Informs the token issuer that the application is able to perform complex authentication actions.
107111
* For example, "cp1" means that the application is able to perform conditional access evaluation,
108-
* because the application has been setup to parse WWW-Authenticate headers associated with a 401 response from the protected APIs,
112+
* because the application has been set up to parse WWW-Authenticate headers associated with a 401 response from the protected APIs,
109113
* and to retry the request with claims API.
110114
*
111115
* @param clientCapabilities a list of capabilities (e.g., ["cp1"]) recognized by the token service.

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ public class ManagedIdentityParameters implements IAcquireTokenParameters {
1515
String resource;
1616
boolean forceRefresh;
1717
String claims;
18-
19-
private ManagedIdentityParameters(String resource, boolean forceRefresh, String claims) {
18+
private String tokenToRevoke;
19+
private ManagedIdentityParameters(String resource, boolean forceRefresh, String claims, String tokenToRevoke) {
2020
this.resource = resource;
2121
this.forceRefresh = forceRefresh;
2222
this.claims = claims;
23+
this.tokenToRevoke = tokenToRevoke;
2324
}
2425

2526
@Override
@@ -78,10 +79,19 @@ public String resource() {
7879
return this.resource;
7980
}
8081

82+
/**
83+
* Gets the token to be revoked in supported Managed Identity environments.
84+
* @return the token to be revoked
85+
*/
86+
public String getTokenToRevoke() {
87+
return this.tokenToRevoke;
88+
}
89+
8190
public static class ManagedIdentityParametersBuilder {
8291
private String resource;
8392
private boolean forceRefresh;
8493
private String claims;
94+
private String tokenToRevoke;
8595

8696
ManagedIdentityParametersBuilder() {
8797
}
@@ -112,9 +122,23 @@ public ManagedIdentityParametersBuilder claims(String claims) {
112122
this.claims = claims;
113123
return this;
114124
}
125+
126+
/**
127+
* Specifies a token to be revoked in supported Managed Identity environments (App Service, Service Fabric).
128+
* The token will be converted to a SHA256 hash for revocation to avoid transmitting the original token.
129+
*
130+
* @param tokenToRevoke The access token to revoke
131+
* @return this builder instance
132+
*/
133+
public ManagedIdentityParametersBuilder tokenToRevoke(String tokenToRevoke) {
134+
ParameterValidationUtils.validateNotBlank("tokenToRevoke", tokenToRevoke);
135+
136+
this.tokenToRevoke = tokenToRevoke;
137+
return this;
138+
}
115139

116140
public ManagedIdentityParameters build() {
117-
return new ManagedIdentityParameters(this.resource, this.forceRefresh, this.claims);
141+
return new ManagedIdentityParameters(this.resource, this.forceRefresh, this.claims, this.tokenToRevoke);
118142
}
119143

120144
public String toString() {

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.net.URISyntaxException;
1313
import java.net.URL;
1414
import java.util.Collections;
15+
import java.util.HashMap;
1516
import java.util.List;
1617
import java.util.Map;
1718

@@ -31,6 +32,36 @@ class ManagedIdentityRequest extends MsalRequest {
3132

3233
public ManagedIdentityRequest(ManagedIdentityApplication managedIdentityApplication, RequestContext requestContext) {
3334
super(managedIdentityApplication, requestContext);
35+
36+
// Check if the environment supports token revocation
37+
ManagedIdentitySourceType sourceType = ManagedIdentityClient.getManagedIdentitySource();
38+
boolean supportsTokenRevocation = false;
39+
40+
for (ManagedIdentitySourceType type : Constants.TOKEN_REVOCATION_SUPPORTED_ENVIRONMENTS) {
41+
if (type == sourceType) {
42+
supportsTokenRevocation = true;
43+
break;
44+
}
45+
}
46+
47+
// If client capabilities include CP1 (token revocation) and the source type supports it,
48+
// add the token revocation parameter to query parameters
49+
if (supportsTokenRevocation &&
50+
managedIdentityApplication.getClientCapabilities() != null &&
51+
managedIdentityApplication.getClientCapabilities().contains(Constants.CLIENT_CAPABILITY_CP1)) {
52+
53+
if (requestContext.apiParameters() instanceof ManagedIdentityParameters) {
54+
ManagedIdentityParameters parameters = (ManagedIdentityParameters) requestContext.apiParameters();
55+
if (parameters.getTokenToRevoke() != null && !parameters.getTokenToRevoke().isEmpty()) {
56+
LOG.info("[Managed Identity] Adding token revocation parameter to request");
57+
if (queryParameters == null) {
58+
queryParameters = new HashMap<>();
59+
}
60+
String tokenHash = TokenRevocationUtil.convertTokenToSHA256HashString(parameters.getTokenToRevoke());
61+
queryParameters.put(Constants.TOKEN_REVOCATION_REQUEST_PARAM, Collections.singletonList(tokenHash));
62+
}
63+
}
64+
}
3465
}
3566

3667
public String getBodyAsString() {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
import java.nio.charset.StandardCharsets;
7+
import java.security.MessageDigest;
8+
import java.security.NoSuchAlgorithmException;
9+
import javax.xml.bind.DatatypeConverter;
10+
11+
/**
12+
* Utility class for token revocation operations
13+
*/
14+
public final class TokenRevocationUtil {
15+
16+
private TokenRevocationUtil() {
17+
// Private constructor to prevent instantiation
18+
}
19+
20+
/**
21+
* Converts a token to its SHA256 hash string representation.
22+
* This is used in token revocation scenarios to identify tokens without transmitting the original token.
23+
*
24+
* @param token The token to hash
25+
* @return The SHA256 hash of the token as a lowercase hex string
26+
* @throws MsalClientException If the SHA-256 algorithm is not available
27+
*/
28+
public static String convertTokenToSHA256HashString(String token) {
29+
if (token == null || token.isEmpty()) {
30+
throw new IllegalArgumentException("Token cannot be null or empty");
31+
}
32+
33+
try {
34+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
35+
byte[] hash = digest.digest(token.getBytes(StandardCharsets.UTF_8));
36+
return DatatypeConverter.printHexBinary(hash).toLowerCase();
37+
} catch (NoSuchAlgorithmException e) {
38+
throw new MsalClientException("Failed to create SHA-256 hash: " + e.getMessage(),
39+
AuthenticationErrorCode.CRYPTO_ERROR);
40+
}
41+
}
42+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.aad.msal4j;
5+
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
import java.util.Arrays;
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
public class TokenRevocationTest {
15+
16+
private static final String TEST_TOKEN = "test_token";
17+
private static final String EXPECTED_TOKEN_HASH = "cc0af97287543b65da2c7e1476426021826cab166f1e063ed012b855ff819656";
18+
private static final String TEST_RESOURCE = "https://management.azure.com";
19+
20+
@Test
21+
public void testConvertTokenToSHA256Hash() {
22+
String hash = TokenRevocationUtil.convertTokenToSHA256HashString(TEST_TOKEN);
23+
assertEquals(EXPECTED_TOKEN_HASH, hash);
24+
}
25+
26+
@Test
27+
public void testTokenToRevokeValidation() {
28+
// Should throw exception when null
29+
assertThrows(IllegalArgumentException.class, () -> {
30+
TokenRevocationUtil.convertTokenToSHA256HashString(null);
31+
});
32+
33+
// Should throw exception when empty
34+
assertThrows(IllegalArgumentException.class, () -> {
35+
TokenRevocationUtil.convertTokenToSHA256HashString("");
36+
});
37+
}
38+
39+
@Test
40+
public void testManagedIdentityParametersBuilder() {
41+
ManagedIdentityParameters params = ManagedIdentityParameters.builder(TEST_RESOURCE)
42+
.tokenToRevoke(TEST_TOKEN)
43+
.build();
44+
45+
assertEquals(TEST_RESOURCE, params.resource());
46+
assertEquals(TEST_TOKEN, params.getTokenToRevoke());
47+
}
48+
49+
@Test
50+
public void testManagedIdentityParametersBuilderValidation() {
51+
// Should throw exception when tokenToRevoke is null or empty
52+
assertThrows(IllegalArgumentException.class, () -> {
53+
ManagedIdentityParameters.builder(TEST_RESOURCE)
54+
.tokenToRevoke(null)
55+
.build();
56+
});
57+
58+
assertThrows(IllegalArgumentException.class, () -> {
59+
ManagedIdentityParameters.builder(TEST_RESOURCE)
60+
.tokenToRevoke("")
61+
.build();
62+
});
63+
}
64+
}

0 commit comments

Comments
 (0)