55
66import org .slf4j .LoggerFactory ;
77
8+ import java .util .Base64 ;
9+ import java .util .Date ;
810import java .util .concurrent .CompletableFuture ;
911import java .util .function .Function ;
1012
2022public 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 *
0 commit comments