@@ -27,7 +27,7 @@ class AcquireTokenByClientCredentialSupplier extends AuthenticationResultSupplie
2727
2828 @ Override
2929 AuthenticationResult execute () throws Exception {
30- prepareMtlsProofOfPossession ();
30+ prepareMtls ();
3131
3232 if (clientCredentialRequest .parameters .skipCache () != null &&
3333 !clientCredentialRequest .parameters .skipCache ()) {
@@ -75,80 +75,124 @@ AuthenticationResult execute() throws Exception {
7575 return acquireTokenByClientCredential ();
7676 }
7777
78- private void prepareMtlsProofOfPossession () throws MalformedURLException {
79- ClientCredentialParameters parameters = clientCredentialRequest .parameters ;
80- if (!parameters .mtlsProofOfPossession ()) {
81- return ;
82- }
83- if (clientCredentialRequest .appTokenProvider != null ) {
84- throw new MsalClientException (
85- "mTLS Proof-of-Possession cannot be used with appTokenProvider." ,
86- AuthenticationErrorCode .MTLS_POP_ERROR );
87- }
78+ private void prepareMtls () throws MalformedURLException {
79+ ClientCredentialParameters parameters = clientCredentialRequest .parameters ;
80+ if (clientCredentialRequest .appTokenProvider != null ) {
81+ if (!parameters .mtlsProofOfPossession ()) {
82+ return ;
83+ }
84+ throw new MsalClientException (
85+ "mTLS Proof-of-Possession cannot be used with appTokenProvider." ,
86+ AuthenticationErrorCode .MTLS_POP_ERROR );
87+ }
88+
89+ ConfidentialClientApplication application = (ConfidentialClientApplication ) clientApplication ;
90+ IClientCredential credential = parameters .clientCredential () != null
91+ ? parameters .clientCredential () : application .clientCredential ;
92+ IClientCertificate bindingCertificate = null ;
93+ if (credential instanceof ClientAssertion
94+ && ((ClientAssertion ) credential ).isSignedAssertionProvider ()) {
95+ ClientSignedAssertion preflight = ((ClientAssertion ) credential ).signedAssertion (
96+ assertionRequestOptions (application ));
97+ bindingCertificate = preflight .getTokenBindingCertificate ();
98+ }
99+
100+ if (!parameters .mtlsProofOfPossession () && bindingCertificate == null ) {
101+ return ;
102+ }
88103
89- Set < String > protocolOwned = new HashSet <>( Arrays . asList (
90- "token_type" , "client_assertion" , "client_assertion_type" , "req_cnf" ));
91- if ( parameters . extraQueryParameters () != null ) {
92- for ( String name : parameters .extraQueryParameters (). keySet () ) {
93- if ( protocolOwned . contains ( name . toLowerCase ( Locale . ROOT ) )) {
94- throw new MsalClientException (
95- "extraQueryParameters cannot override protocol-owned parameter '" + name + "'." ,
96- AuthenticationErrorCode . MTLS_POP_ERROR );
97- }
104+ if ( parameters . mtlsProofOfPossession ()) {
105+ Set < String > protocolOwned = new HashSet <>( Arrays . asList (
106+ "token_type" , "client_assertion" , "client_assertion_type" , "req_cnf" ));
107+ if ( parameters .extraQueryParameters () != null ) {
108+ for ( String name : parameters . extraQueryParameters (). keySet ( )) {
109+ if ( protocolOwned . contains ( name . toLowerCase ( Locale . ROOT ))) {
110+ throw new MsalClientException (
111+ "extraQueryParameters cannot override protocol-owned parameter '" + name + "'." ,
112+ AuthenticationErrorCode . MTLS_POP_ERROR );
98113 }
99114 }
115+ }
116+ }
100117
101- ConfidentialClientApplication application = (ConfidentialClientApplication ) clientApplication ;
102- Authority authority = application .authenticationAuthority ;
103- if (parameters .tenant () != null ) {
104- authority = Authority .replaceTenant (authority , parameters .tenant ());
105- }
106- if (authority .authorityType () != AuthorityType .AAD ) {
107- throw new MsalClientException (
108- "mTLS Proof-of-Possession requires a Microsoft Entra AAD authority." ,
109- AuthenticationErrorCode .MTLS_POP_ERROR );
110- }
111- MtlsEndpointHelper .deriveMtlsTokenEndpoint (authority .tokenEndpointUrl ());
112-
113- IClientCredential credential = parameters .clientCredential () != null
114- ? parameters .clientCredential () : application .clientCredential ;
115- if (!(credential instanceof IClientCertificate )) {
116- throw new MsalClientException (
117- "mTLS Proof-of-Possession requires a certificate credential." ,
118- AuthenticationErrorCode .MTLS_POP_ERROR );
119- }
120- if (!(application .httpClient () instanceof IMtlsCapableHttpClient )) {
121- throw new MsalClientException (
122- "The configured custom HTTP client does not support request-specific mTLS. "
123- + "Implement IMtlsCapableHttpClient and honor the request TLS and redirect settings." ,
124- AuthenticationErrorCode .MTLS_POP_ERROR );
125- }
126- if (application .httpClient () instanceof DefaultHttpClient
127- && application .sslSocketFactory () != null ) {
128- throw new MsalClientException (
129- "mTLS Proof-of-Possession cannot safely compose the configured application "
130- + "SSLSocketFactory with the binding certificate. Use an "
131- + "IMtlsCapableHttpClient that combines HttpRequest.sslContext() with "
132- + "the application's trust policy." ,
133- AuthenticationErrorCode .MTLS_POP_ERROR );
134- }
118+ Authority authority = application .authenticationAuthority ;
119+ if (parameters .tenant () != null ) {
120+ authority = Authority .replaceTenant (authority , parameters .tenant ());
121+ }
122+ if (authority .authorityType () != AuthorityType .AAD ) {
123+ throw new MsalClientException (
124+ "mTLS authentication requires a Microsoft Entra AAD authority." ,
125+ AuthenticationErrorCode .MTLS_POP_ERROR );
126+ }
127+ MtlsEndpointHelper .deriveMtlsTokenEndpoint (authority .tokenEndpointUrl ());
135128
136- MtlsBindingContext bindingContext = MtlsBindingContext .create ((IClientCertificate ) credential );
137- clientCredentialRequest .mtlsBindingContext (bindingContext );
138- SortedMap <String , String > components = new TreeMap <>();
139- components .put ("token_type" , TokenType .MTLS_POP .value ());
140- components .put ("cert_der_hash" , bindingContext .keyId ());
141- String baseHash = parameters .computeExtCacheKeyHash ();
142- if (!StringHelper .isBlank (baseHash )) {
143- components .put ("parameters" , baseHash );
144- }
145- clientCredentialRequest .acquisitionCacheKeyHash (
146- StringHelper .computeExtCacheKeyHash (components ));
129+ if (bindingCertificate == null && credential instanceof IClientCertificate ) {
130+ bindingCertificate = (IClientCertificate ) credential ;
131+ }
132+ if (bindingCertificate == null ) {
133+ if (credential instanceof ClientAssertion
134+ && ((ClientAssertion ) credential ).isSignedAssertionProvider ()) {
135+ throw new MsalClientException (
136+ "mTLS Proof-of-Possession requires a certificate for this request. "
137+ + "Either configure the application with a certificate or return a "
138+ + "certificate-bound client assertion and request mTLS Proof-of-Possession. "
139+ + "See https://aka.ms/msal-net-pop for details." ,
140+ AuthenticationErrorCode .MTLS_CERTIFICATE_NOT_PROVIDED );
147141 }
142+ throw new MsalClientException (
143+ "mTLS Proof-of-Possession requires a certificate credential." ,
144+ AuthenticationErrorCode .MTLS_POP_ERROR );
145+ }
146+ if (!(application .httpClient () instanceof IMtlsCapableHttpClient )) {
147+ throw new MsalClientException (
148+ "The configured custom HTTP client does not support request-specific mTLS. "
149+ + "Implement IMtlsCapableHttpClient and honor the request TLS and redirect settings." ,
150+ AuthenticationErrorCode .MTLS_POP_ERROR );
151+ }
152+ if (application .httpClient () instanceof DefaultHttpClient
153+ && application .sslSocketFactory () != null ) {
154+ throw new MsalClientException (
155+ "mTLS authentication cannot safely compose the configured application "
156+ + "SSLSocketFactory with the binding certificate. Use an "
157+ + "IMtlsCapableHttpClient that combines HttpRequest.sslContext() with "
158+ + "the application's trust policy." ,
159+ AuthenticationErrorCode .MTLS_POP_ERROR );
160+ }
161+
162+ MtlsBindingContext bindingContext = MtlsBindingContext .create (bindingCertificate );
163+ if (parameters .mtlsProofOfPossession ()) {
164+ clientCredentialRequest .mtlsBindingContext (bindingContext );
165+ } else {
166+ clientCredentialRequest .clientAssertionMtlsBindingContext (bindingContext );
167+ return ;
168+ }
169+ SortedMap <String , String > components = new TreeMap <>();
170+ components .put ("token_type" , TokenType .MTLS_POP .value ());
171+ components .put ("cert_der_hash" , bindingContext .keyId ());
172+ String baseHash = parameters .computeExtCacheKeyHash ();
173+ if (!StringHelper .isBlank (baseHash )) {
174+ components .put ("parameters" , baseHash );
175+ }
176+ clientCredentialRequest .acquisitionCacheKeyHash (
177+ StringHelper .computeExtCacheKeyHash (components ));
178+ }
179+
180+ private AssertionRequestOptions assertionRequestOptions (
181+ ConfidentialClientApplication application ) throws MalformedURLException {
182+ Authority authority = application .authenticationAuthority ;
183+ if (clientCredentialRequest .parameters .tenant () != null ) {
184+ authority = Authority .replaceTenant (
185+ authority , clientCredentialRequest .parameters .tenant ());
186+ }
187+ return new AssertionRequestOptions (
188+ application .clientId (),
189+ authority .tokenEndpointUrl ().toString (),
190+ clientCredentialRequest .parameters .fmiPath ());
191+ }
148192
149193 private AuthenticationResult reattachMtlsBinding (AuthenticationResult result ) {
150- MtlsBindingContext context = clientCredentialRequest .mtlsBindingContext ();
151- return context == null ? result : result .withMtlsBindingContext (context );
194+ MtlsBindingContext context = clientCredentialRequest .mtlsBindingContext ();
195+ return context == null ? result : result .withMtlsBindingContext (context );
152196 }
153197
154198 private AuthenticationResult acquireTokenByClientCredential () throws Exception {
0 commit comments