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