Skip to content

Commit e356bb6

Browse files
committed
Re-add behavior for refreshing assertions
1 parent 5a4f9fc commit e356bb6

File tree

3 files changed

+53
-13
lines changed

3 files changed

+53
-13
lines changed

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

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import org.slf4j.LoggerFactory;
77

8+
import java.util.Base64;
9+
import java.util.Date;
810
import java.util.concurrent.CompletableFuture;
911
import java.util.function.Function;
1012

@@ -20,7 +22,8 @@
2022
public class ConfidentialClientApplication extends AbstractClientApplicationBase implements IConfidentialClientApplication {
2123

2224
private ClientCertificate clientCertificate;
23-
String assertion;
25+
private String assertion;
26+
private IClientCredential clientCredential;
2427
String secret;
2528

2629
/** AppTokenProvider creates a Credential from a function that provides access tokens. The function
@@ -81,6 +84,8 @@ private ConfidentialClientApplication(Builder builder) {
8184
private void initClientAuthentication(IClientCredential clientCredential) {
8285
validateNotNull("clientCredential", clientCredential);
8386

87+
this.clientCredential = clientCredential;
88+
8489
if (clientCredential instanceof ClientSecret) {
8590
this.secret = ((ClientSecret) clientCredential).clientSecret();
8691
} else if (clientCredential instanceof ClientCertificate) {
@@ -93,23 +98,58 @@ private void initClientAuthentication(IClientCredential clientCredential) {
9398
}
9499
}
95100

101+
/**
102+
* Generates a JWT-formatted assertion string based on the provided client credential. Returns null in cases where
103+
* the request for that credential type would not use a JWT assertion (e.g. client secret).
104+
*
105+
* @param clientCredential The client credential to use for token acquisition.
106+
* @return JWT-formatted assertion string
107+
*/
96108
String getAssertionString(IClientCredential clientCredential) {
97109
if (clientCredential instanceof ClientCertificate) {
98-
boolean useSha1 = Authority.detectAuthorityType(this.authenticationAuthority.canonicalAuthorityUrl()) == AuthorityType.ADFS;
99-
100-
return JwtHelper.buildJwt(
101-
clientId(),
102-
clientCertificate,
103-
this.authenticationAuthority.selfSignedJwtAudience(),
104-
sendX5c,
105-
useSha1).assertion();
110+
// Check if the current assertion is null or has expired, and if so create a new one
111+
if (this.assertion == null || hasJwtExpired(this.assertion)) {
112+
boolean useSha1 = Authority.detectAuthorityType(this.authenticationAuthority.canonicalAuthorityUrl()) == AuthorityType.ADFS;
113+
114+
this.assertion = JwtHelper.buildJwt(
115+
clientId(),
116+
clientCertificate,
117+
this.authenticationAuthority.selfSignedJwtAudience(),
118+
sendX5c,
119+
useSha1).assertion();
120+
}
121+
return this.assertion;
106122
} else if (clientCredential instanceof ClientAssertion) {
107123
return ((ClientAssertion) clientCredential).assertion();
124+
} else if (clientCredential instanceof ClientSecret) {
125+
return null;
108126
} else {
109127
throw new IllegalArgumentException("Unsupported client credential");
110128
}
111129
}
112130

131+
//Overload for the common case where the application's default credential was not overridden in the request.
132+
String getAssertionString() {
133+
return this.getAssertionString(this.clientCredential);
134+
}
135+
136+
/**
137+
* Checks if the JWT-formatted assertion has expired by parsing the "exp" claim.
138+
*
139+
* @param jwt JWT string
140+
* @return true if the JWT has expired. Otherwise false
141+
*/
142+
boolean hasJwtExpired(String jwt) {
143+
final Date currentDateTime = new Date(System.currentTimeMillis());
144+
Base64.Decoder decoder = Base64.getUrlDecoder();
145+
146+
String payload = new String(decoder.decode(jwt.split("\\.")[1]));
147+
148+
final Date expirationTime = (Date) JsonHelper.parseJsonToMap(payload).get("exp");
149+
150+
return expirationTime.before(currentDateTime);
151+
}
152+
113153
/**
114154
* Creates instance of Builder of ConfidentialClientApplication
115155
*

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ private void addQueryParameters(OAuthHttpRequest oauthHttpRequest) {
9393
IClientCredential credential = ((ClientCredentialRequest) msalRequest).parameters.clientCredential();
9494
addJWTBearerAssertionParams(queryParameters, ((ConfidentialClientApplication) msalRequest.application()).getAssertionString(credential));
9595
} else {
96-
if (((ConfidentialClientApplication) msalRequest.application()).assertion != null) {
97-
addJWTBearerAssertionParams(queryParameters, ((ConfidentialClientApplication) msalRequest.application()).assertion);
96+
if (((ConfidentialClientApplication) msalRequest.application()).getAssertionString() != null) {
97+
addJWTBearerAssertionParams(queryParameters, ((ConfidentialClientApplication) msalRequest.application()).getAssertionString());
9898
} else if (((ConfidentialClientApplication) msalRequest.application()).secret != null) {
9999
// Client secrets have a different parameter than bearer assertions
100100
queryParameters.put("client_secret", ((ConfidentialClientApplication) msalRequest.application()).secret);

msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/ClientCertificateTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ void testIClientCertificateInterface_CredentialFactoryUsesSha256() throws Except
7777
HttpRequest request = parameters.getArgument(0);
7878
String requestBody = request.body();
7979

80-
SignedJWT signedJWT = SignedJWT.parse(cca.assertion);
80+
SignedJWT signedJWT = SignedJWT.parse(cca.getAssertionString());
8181

82-
if (requestBody.contains(cca.assertion)
82+
if (requestBody.contains(cca.getAssertionString())
8383
&& signedJWT.getHeader().toJSONObject().containsKey("x5t#S256")) {
8484
return TestHelper.expectedResponse(200, TestHelper.getSuccessfulTokenResponse(tokenResponseValues));
8585
}

0 commit comments

Comments
 (0)