@@ -210,6 +210,169 @@ def test_pem_with_neither_raises_error(self, mock_jwt_creator_class, mock_author
210210 self .assertIn ("thumbprint" , str (context .exception ).lower ())
211211 self .assertIn ("public_certificate" , str (context .exception ).lower ())
212212
213+ def test_pem_with_thumbprint_sha256_only_uses_sha256 (
214+ self , mock_jwt_creator_class , mock_authority_class ):
215+ """Test that providing only thumbprint_sha256 uses SHA-256"""
216+ authority = "https://login.microsoftonline.com/common"
217+ self ._setup_mocks (mock_authority_class , authority )
218+
219+ # Create app with only SHA256 thumbprint
220+ sha256_thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
221+ app = ConfidentialClientApplication (
222+ client_id = "my_client_id" ,
223+ client_credential = {
224+ "private_key" : self .test_private_key ,
225+ "thumbprint_sha256" : sha256_thumbprint ,
226+ },
227+ authority = authority
228+ )
229+
230+ # Verify SHA-256 with PS256 algorithm is used
231+ self ._verify_assertion_params (
232+ mock_jwt_creator_class ,
233+ expected_algorithm = 'PS256' ,
234+ expected_thumbprint_type = 'sha256'
235+ )
236+
237+ def test_pem_with_both_thumbprints_aad_uses_sha256 (
238+ self , mock_jwt_creator_class , mock_authority_class ):
239+ """Test that with both thumbprints, AAD authority uses SHA-256"""
240+ authority = "https://login.microsoftonline.com/common"
241+ self ._setup_mocks (mock_authority_class , authority )
242+
243+ # Create app with BOTH thumbprints for AAD
244+ sha1_thumbprint = "A1B2C3D4E5F6"
245+ sha256_thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
246+ app = ConfidentialClientApplication (
247+ client_id = "my_client_id" ,
248+ client_credential = {
249+ "private_key" : self .test_private_key ,
250+ "thumbprint" : sha1_thumbprint ,
251+ "thumbprint_sha256" : sha256_thumbprint ,
252+ },
253+ authority = authority
254+ )
255+
256+ # For AAD, should use SHA-256 when both are provided
257+ self ._verify_assertion_params (
258+ mock_jwt_creator_class ,
259+ expected_algorithm = 'PS256' ,
260+ expected_thumbprint_type = 'sha256'
261+ )
262+
263+ def test_pem_with_both_thumbprints_adfs_uses_sha1 (
264+ self , mock_jwt_creator_class , mock_authority_class ):
265+ """Test that with both thumbprints, ADFS authority uses SHA-1"""
266+ authority = "https://adfs.contoso.com/adfs"
267+ self ._setup_mocks (mock_authority_class , authority )
268+
269+ # Create app with BOTH thumbprints for ADFS
270+ sha1_thumbprint = "A1B2C3D4E5F6"
271+ sha256_thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
272+ app = ConfidentialClientApplication (
273+ client_id = "my_client_id" ,
274+ client_credential = {
275+ "private_key" : self .test_private_key ,
276+ "thumbprint" : sha1_thumbprint ,
277+ "thumbprint_sha256" : sha256_thumbprint ,
278+ },
279+ authority = authority
280+ )
281+
282+ # For ADFS, should use SHA-1 when both are provided
283+ self ._verify_assertion_params (
284+ mock_jwt_creator_class ,
285+ expected_algorithm = 'RS256' ,
286+ expected_thumbprint_type = 'sha1' ,
287+ expected_thumbprint_value = sha1_thumbprint
288+ )
289+
290+ def test_pem_with_both_thumbprints_b2c_uses_sha256 (
291+ self , mock_jwt_creator_class , mock_authority_class ):
292+ """Test that with both thumbprints, B2C authority uses SHA-256"""
293+ authority = "https://contoso.b2clogin.com/contoso.onmicrosoft.com/B2C_1_susi"
294+ mock_authority = self ._setup_mocks (mock_authority_class , authority )
295+
296+ # Manually set _is_b2c to True for this B2C authority
297+ mock_authority ._is_b2c = True
298+
299+ # Create app with BOTH thumbprints for B2C
300+ sha1_thumbprint = "A1B2C3D4E5F6"
301+ sha256_thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
302+ app = ConfidentialClientApplication (
303+ client_id = "my_client_id" ,
304+ client_credential = {
305+ "private_key" : self .test_private_key ,
306+ "thumbprint" : sha1_thumbprint ,
307+ "thumbprint_sha256" : sha256_thumbprint ,
308+ },
309+ authority = authority
310+ )
311+
312+ # For B2C, should use SHA-256 when both are provided
313+ self ._verify_assertion_params (
314+ mock_jwt_creator_class ,
315+ expected_algorithm = 'PS256' ,
316+ expected_thumbprint_type = 'sha256'
317+ )
318+
319+ def test_pem_with_both_thumbprints_ciam_uses_sha256 (
320+ self , mock_jwt_creator_class , mock_authority_class ):
321+ """Test that with both thumbprints, CIAM authority uses SHA-256"""
322+ authority = "https://contoso.ciamlogin.com/contoso.onmicrosoft.com"
323+ mock_authority = self ._setup_mocks (mock_authority_class , authority )
324+
325+ # Create app with BOTH thumbprints for CIAM
326+ sha1_thumbprint = "A1B2C3D4E5F6"
327+ sha256_thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
328+ app = ConfidentialClientApplication (
329+ client_id = "my_client_id" ,
330+ client_credential = {
331+ "private_key" : self .test_private_key ,
332+ "thumbprint" : sha1_thumbprint ,
333+ "thumbprint_sha256" : sha256_thumbprint ,
334+ },
335+ authority = authority
336+ )
337+
338+ # For CIAM, should use SHA-256 when both are provided
339+ self ._verify_assertion_params (
340+ mock_jwt_creator_class ,
341+ expected_algorithm = 'PS256' ,
342+ expected_thumbprint_type = 'sha256'
343+ )
344+
345+ def test_pem_with_both_thumbprints_generic_uses_sha1 (
346+ self , mock_jwt_creator_class , mock_authority_class ):
347+ """Test that with both thumbprints, generic authority uses SHA-1"""
348+ authority = "https://custom.authority.com/tenant"
349+ mock_authority = self ._setup_mocks (mock_authority_class , authority )
350+
351+ # Set up as a generic authority (not ADFS, not B2C, not in known hosts)
352+ mock_authority .is_adfs = False
353+ mock_authority ._is_b2c = False
354+
355+ # Create app with BOTH thumbprints for generic authority
356+ sha1_thumbprint = "A1B2C3D4E5F6"
357+ sha256_thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
358+ app = ConfidentialClientApplication (
359+ client_id = "my_client_id" ,
360+ client_credential = {
361+ "private_key" : self .test_private_key ,
362+ "thumbprint" : sha1_thumbprint ,
363+ "thumbprint_sha256" : sha256_thumbprint ,
364+ },
365+ authority = authority
366+ )
367+
368+ # For generic authorities, should use SHA-1 when both are provided
369+ self ._verify_assertion_params (
370+ mock_jwt_creator_class ,
371+ expected_algorithm = 'RS256' ,
372+ expected_thumbprint_type = 'sha1' ,
373+ expected_thumbprint_value = sha1_thumbprint
374+ )
375+
213376
214377if __name__ == "__main__" :
215378 unittest .main ()
0 commit comments