@@ -107,4 +107,81 @@ void OnBehalfOf_TenantOverride() throws Exception {
107107 assertNotEquals (resultAppLevelTenant .accessToken (), resultRequestLevelTenant .accessToken ());
108108 verify (httpClientMock , times (2 )).send (any ());
109109 }
110+
111+ @ Test
112+ void testCredentialPrecedenceAndMixing () throws Exception {
113+ DefaultHttpClient httpClientMock = mock (DefaultHttpClient .class );
114+
115+ // Create different credential types for testing
116+ IClientCredential appLevelCredential = ClientCredentialFactory .createFromSecret ("appLevelSecret" );
117+ IClientCredential requestLevelSecret = ClientCredentialFactory .createFromSecret ("requestLevelSecret" );
118+ String assertionValue = "test_assertion_value" ;
119+ IClientCredential requestLevelAssertion = ClientCredentialFactory .createFromClientAssertion (assertionValue );
120+
121+ // Create the application with the app-level credential
122+ ConfidentialClientApplication cca =
123+ ConfidentialClientApplication .builder ("clientId" , appLevelCredential )
124+ .authority ("https://login.microsoftonline.com/tenant" )
125+ .instanceDiscovery (false )
126+ .validateAuthority (false )
127+ .httpClient (httpClientMock )
128+ .build ();
129+
130+ // Set up the mock to check which credential is being used
131+ when (httpClientMock .send (any (HttpRequest .class ))).thenAnswer (invocation -> {
132+ HttpRequest request = invocation .getArgument (0 );
133+ String requestBody = request .body ();
134+
135+ // Check which credential type is included in the request and return a matching token
136+ if (requestBody .contains ("client_secret=requestLevelSecret" )) {
137+ HashMap <String , String > responseParams = new HashMap <>();
138+ responseParams .put ("access_token" , "request_secret_token" );
139+ return TestHelper .expectedResponse (HttpStatus .HTTP_OK ,
140+ TestHelper .getSuccessfulTokenResponse (responseParams ));
141+ } else if (requestBody .contains ("client_secret=appLevelSecret" )) {
142+ HashMap <String , String > responseParams = new HashMap <>();
143+ responseParams .put ("access_token" , "app_secret_token" );
144+ return TestHelper .expectedResponse (HttpStatus .HTTP_OK ,
145+ TestHelper .getSuccessfulTokenResponse (responseParams ));
146+ } else if (requestBody .contains ("client_assertion=" + assertionValue )) {
147+ HashMap <String , String > responseParams = new HashMap <>();
148+ responseParams .put ("access_token" , "assertion_token" );
149+ return TestHelper .expectedResponse (HttpStatus .HTTP_OK ,
150+ TestHelper .getSuccessfulTokenResponse (responseParams ));
151+ }
152+ return null ;
153+ });
154+
155+ // Test 1: Request with same credential type (secret) at request level
156+ ClientCredentialParameters parametersWithRequestSecret =
157+ ClientCredentialParameters .builder (Collections .singleton ("scope" ))
158+ .clientCredential (requestLevelSecret )
159+ .skipCache (true )
160+ .build ();
161+
162+ IAuthenticationResult result1 = cca .acquireToken (parametersWithRequestSecret ).get ();
163+ assertEquals ("request_secret_token" , result1 .accessToken (),
164+ "Request-level secret should be used when provided" );
165+
166+ // Test 2: Request with different credential type (assertion) at request level
167+ ClientCredentialParameters parametersWithAssertion =
168+ ClientCredentialParameters .builder (Collections .singleton ("scope" ))
169+ .clientCredential (requestLevelAssertion )
170+ .skipCache (true )
171+ .build ();
172+
173+ IAuthenticationResult result2 = cca .acquireToken (parametersWithAssertion ).get ();
174+ assertEquals ("assertion_token" , result2 .accessToken (),
175+ "Request-level assertion should be used when provided" );
176+
177+ // Test 3: Request without credential specified should fall back to app-level
178+ ClientCredentialParameters parametersWithoutCredential =
179+ ClientCredentialParameters .builder (Collections .singleton ("scope" ))
180+ .skipCache (true )
181+ .build ();
182+
183+ IAuthenticationResult result3 = cca .acquireToken (parametersWithoutCredential ).get ();
184+ assertEquals ("app_secret_token" , result3 .accessToken (),
185+ "App-level credential should be used when request-level credential is not provided" );
186+ }
110187}
0 commit comments