1010final class ClientAssertion implements IClientAssertion {
1111
1212 static final String ASSERTION_TYPE_JWT_BEARER = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" ;
13+ static final String ASSERTION_TYPE_JWT_POP = "urn:ietf:params:oauth:client-assertion-type:jwt-pop" ;
1314 private final String assertion ;
1415 private final Callable <String > assertionProvider ;
1516 private final Function <AssertionRequestOptions , String > contextAwareAssertionProvider ;
17+ private final Function <AssertionRequestOptions , AssertionResponse > contextAwareResponseProvider ;
1618
1719 /**
1820 * Constructor that accepts a static assertion string
@@ -28,6 +30,7 @@ final class ClientAssertion implements IClientAssertion {
2830 this .assertion = assertion ;
2931 this .assertionProvider = null ;
3032 this .contextAwareAssertionProvider = null ;
33+ this .contextAwareResponseProvider = null ;
3134 }
3235
3336 /**
@@ -44,6 +47,7 @@ final class ClientAssertion implements IClientAssertion {
4447 this .assertion = null ;
4548 this .assertionProvider = assertionProvider ;
4649 this .contextAwareAssertionProvider = null ;
50+ this .contextAwareResponseProvider = null ;
4751 }
4852
4953 /**
@@ -62,6 +66,27 @@ final class ClientAssertion implements IClientAssertion {
6266 this .assertion = null ;
6367 this .assertionProvider = null ;
6468 this .contextAwareAssertionProvider = contextAwareAssertionProvider ;
69+ this .contextAwareResponseProvider = null ;
70+ }
71+
72+ /**
73+ * Constructor that accepts a context-aware function returning an {@link AssertionResponse}.
74+ * This allows the callback to supply both the assertion JWT and an optional token-binding
75+ * certificate for mTLS PoP scenarios.
76+ *
77+ * @param contextAwareResponseProvider A function that receives context and returns an AssertionResponse
78+ * @throws NullPointerException if contextAwareResponseProvider is null
79+ */
80+ ClientAssertion (final Function <AssertionRequestOptions , AssertionResponse > contextAwareResponseProvider ,
81+ boolean responseProvider ) {
82+ if (contextAwareResponseProvider == null ) {
83+ throw new NullPointerException ("contextAwareResponseProvider" );
84+ }
85+
86+ this .assertion = null ;
87+ this .assertionProvider = null ;
88+ this .contextAwareAssertionProvider = null ;
89+ this .contextAwareResponseProvider = contextAwareResponseProvider ;
6590 }
6691
6792 /**
@@ -74,6 +99,11 @@ final class ClientAssertion implements IClientAssertion {
7499 * @throws MsalClientException if the assertion provider returns null/empty or throws an exception
75100 */
76101 public String assertion () {
102+ if (contextAwareResponseProvider != null ) {
103+ AssertionResponse response = assertionResponse (new AssertionRequestOptions (null , null , null ));
104+ return response .assertion ();
105+ }
106+
77107 if (contextAwareAssertionProvider != null ) {
78108 return assertion (new AssertionRequestOptions (null , null , null ));
79109 }
@@ -95,6 +125,11 @@ public String assertion() {
95125 * @throws MsalClientException if the assertion provider returns null/empty or throws an exception
96126 */
97127 String assertion (AssertionRequestOptions options ) {
128+ if (contextAwareResponseProvider != null ) {
129+ AssertionResponse response = assertionResponse (options );
130+ return response .assertion ();
131+ }
132+
98133 if (contextAwareAssertionProvider != null ) {
99134 try {
100135 String generatedAssertion = contextAwareAssertionProvider .apply (options );
@@ -118,10 +153,40 @@ String assertion(AssertionRequestOptions options) {
118153 }
119154
120155 /**
121- * Returns true if this assertion uses a context-aware provider.
156+ * Gets the full AssertionResponse from the context-aware response provider.
157+ * Returns null if this ClientAssertion does not use a response provider.
158+ *
159+ * @param options context information for the assertion request
160+ * @return An AssertionResponse, or null if not using a response provider
161+ * @throws MsalClientException if the provider returns null or throws an exception
162+ */
163+ AssertionResponse assertionResponse (AssertionRequestOptions options ) {
164+ if (contextAwareResponseProvider == null ) {
165+ return null ;
166+ }
167+
168+ try {
169+ AssertionResponse response = contextAwareResponseProvider .apply (options );
170+
171+ if (response == null || StringHelper .isBlank (response .assertion ())) {
172+ throw new MsalClientException (
173+ "Assertion provider returned null or empty assertion" ,
174+ AuthenticationErrorCode .INVALID_JWT );
175+ }
176+
177+ return response ;
178+ } catch (MsalClientException ex ) {
179+ throw ex ;
180+ } catch (Exception ex ) {
181+ throw new MsalClientException (ex );
182+ }
183+ }
184+
185+ /**
186+ * Returns true if this assertion uses a context-aware provider (either string or response).
122187 */
123188 boolean isContextAware () {
124- return contextAwareAssertionProvider != null ;
189+ return contextAwareAssertionProvider != null || contextAwareResponseProvider != null ;
125190 }
126191
127192 private String invokeCallable () {
@@ -151,6 +216,11 @@ public boolean equals(Object o) {
151216
152217 ClientAssertion other = (ClientAssertion ) o ;
153218
219+ // For context-aware response providers, we consider them equal if they're the same object
220+ if (this .contextAwareResponseProvider != null && other .contextAwareResponseProvider != null ) {
221+ return this .contextAwareResponseProvider == other .contextAwareResponseProvider ;
222+ }
223+
154224 // For context-aware providers, we consider them equal if they're the same object
155225 if (this .contextAwareAssertionProvider != null && other .contextAwareAssertionProvider != null ) {
156226 return this .contextAwareAssertionProvider == other .contextAwareAssertionProvider ;
@@ -167,6 +237,11 @@ public boolean equals(Object o) {
167237
168238 @ Override
169239 public int hashCode () {
240+ // For context-aware response providers, use the provider's identity hash code
241+ if (contextAwareResponseProvider != null ) {
242+ return System .identityHashCode (contextAwareResponseProvider );
243+ }
244+
170245 // For context-aware providers, use the provider's identity hash code
171246 if (contextAwareAssertionProvider != null ) {
172247 return System .identityHashCode (contextAwareAssertionProvider );
0 commit comments