-
Notifications
You must be signed in to change notification settings - Fork 219
Add SHA256 certificate thumbprint support with authority-based selection#873
Add SHA256 certificate thumbprint support with authority-based selection #873Copilot wants to merge 9 commits into
Conversation
...lection Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
bgavrilMS
commented
Feb 14, 2026
@copilot Ok. this is not good. Let's start over. Start by analyzing the tests for various types of authority. You should be able to identify examples for: adfs, ciam, b2c and a "generic' authority. Find these and show them to me
@copilot Ok. this is not good. Let's start over. Start by analyzing the tests for various types of authority. You should be able to identify examples for: adfs, ciam, b2c and a "generic' authority. Fi...
I've analyzed the test files and found examples of each authority type:
Authority Type Examples from Tests
1. ADFS Authority
- URL Pattern:
https://contoso.com/adfs - Examples in tests:
tests/test_authority.py:249:"https://contoso.com/adfs"tests/test_application.py:821:"https://contoso.com/adfs"
- Characteristics: Tenant path segment is "adfs" (case-insensitive)
2. B2C Authority
- URL Pattern:
https://<tenant>.b2clogin.com/<tenant>/<policy> - Examples in tests:
tests/test_authority.py:258:"https://login.b2clogin.com/contoso/b2c_policy"tests/test_application.py:830:"https://contoso.b2clogin.com/contoso/policy"
- Characteristics: Host ends with
.b2clogin.com(or other WELL_KNOWN_B2C_HOSTS:.b2clogin.cn,.b2clogin.us,.b2clogin.de)
3. CIAM Authority
- URL Pattern:
https://<tenant>.ciamlogin.comorhttps://<tenant>.ciamlogin.com/<path> - Examples in tests:
tests/test_authority.py:90:"https://contoso.ciamlogin.com"tests/test_authority.py:96:"https://contoso.ciamlogin.com/anything"
- Characteristics: Host ends with
.ciamlogin.com
4. Generic/Private Cloud Authority
- URL Pattern: Any custom domain not matching AAD/B2C/CIAM/ADFS patterns
- Examples in tests:
tests/test_authority.py:269:"https://private.cloud/foo"(labeled as "Private Cloud")tests/test_authority.py:67:"https://example.com/tenant_doesnt_matter_in_this_case"tests/test_authority.py:241:"https://contoso.com/common"(with validate_authority=False)
- Characteristics: Not in WELL_KNOWN_AUTHORITY_HOSTS, not ADFS, not B2C/CIAM
5. AAD Authority (for reference)
- URL Pattern:
https://<known-host>/<tenant> - Known hosts (from
msal/authority.py:18-23):login.microsoftonline.com(WORLD_WIDE/AZURE_PUBLIC)login.chinacloudapi.cn(AZURE_CHINA)login-us.microsoftonline.comlogin.microsoftonline.us(AZURE_US_GOVERNMENT)
- Example:
"https://login.microsoftonline.com/common"
Let me know what adjustments you'd like me to make based on these findings.
bgavrilMS
commented
Feb 14, 2026
@copilot For generic authority, see the public API oidc_authority. This represents a generic OIDC complain authority.
Ok, so now you know all the authority types: adfs, b2c, ciam, oidc generic authority. Everything else is an AAD authority.
Note that the library does not know all the authority hosts, i.e. clouds. You can only use the clasification above to know.
Reimplement the solution using that clasification. Also try to reduce code duplication when it comes to authority types.
- Add _is_oidc flag to Authority class to distinguish OIDC generic authorities - Simplify thumbprint selection logic: use SHA256 for all except ADFS and OIDC - Authority classification now: * ADFS: authority.is_adfs → SHA1 * B2C: authority._is_b2c (not OIDC) → SHA256 * CIAM: authority._is_b2c (not OIDC) → SHA256 * OIDC generic: authority._is_oidc → SHA1 * AAD: everything else → SHA256 - Update tests to reflect new classification - Add test for unknown AAD authority (sovereign cloud) Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
@copilot For generic authority, see the public API oidc_authority. This represents a generic OIDC complain authority.
Ok, so now you know all the authority types: adfs, b2c, ciam, oidc generic aut...
Reimplemented using the correct authority classification. Commit 0a8d2be.
Authority Classification:
- ADFS:
authority.is_adfs→ SHA1 - B2C:
authority._is_b2c(and not OIDC) → SHA256 - CIAM:
authority._is_b2c(and not OIDC) → SHA256 - OIDC generic:
authority._is_oidc→ SHA1 - AAD: everything else (including unknown clouds) → SHA256
Changes:
- Added
_is_oidcflag to Authority class - Simplified logic to:
use_sha256 = not authority.is_adfs and not is_oidc - No dependency on WELL_KNOWN_AUTHORITY_HOSTS
- Works with any AAD cloud (sovereign, private, etc.)
- All 12 tests passing
bgavrilMS
commented
Feb 14, 2026
@copilot There is one more authority - dSTS. Use SHA1 for that.
- Update comments to explicitly mention dSTS as part of OIDC generic - Add test for dSTS authority to verify SHA1 is used - dSTS is already handled correctly as it's treated as OIDC authority - All 13 tests passing Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
@copilot There is one more authority - dSTS. Use SHA1 for that.
Added dSTS support. Commit a9fb14e.
dSTS is already handled correctly - it's automatically treated as an OIDC authority (path starts with /dstsv2), which means it gets _is_oidc = True and uses SHA1.
Updated comments to explicitly mention dSTS and added test test_pem_with_both_thumbprints_dsts_uses_sha1 to verify behavior. All 13 tests passing.
bgavrilMS
commented
Feb 17, 2026
@copilot Can you identify unit tests that look at all these types of authorities?
@copilot Can you identify unit tests that look at all these types of authorities?
Unit tests covering all authority types:
CIAM Authority
tests/test_authority.py::TestCiamAuthoritytest_path_less_authority_should_work(line 89)test_authority_with_path_should_be_used_as_is(line 95)
OIDC Generic Authority
tests/test_authority.py::OidcAuthorityTestCasetest_authority_obj_should_do_oidc_discovery_and_skip_instance_discovery(line 118)test_application_obj_should_do_oidc_discovery_and_skip_instance_discovery(line 127)
dSTS Authority
tests/test_authority.py::DstsAuthorityTestCase(inherits from OidcAuthorityTestCase)test_application_obj_should_accept_dsts_url_as_an_authority(line 153)- Plus inherited tests from OidcAuthorityTestCase
ADFS Authority
tests/test_authority.py::TestAuthorityInternalHelperUserRealmDiscoverytest_by_default_adfs_should_skip_validation_and_instance_metadata(line 246)
tests/test_application.py::TestBrokertest_should_fallback_to_non_broker_when_using_adfs(line 818)
B2C Authority
tests/test_authority.py::TestAuthorityInternalHelperUserRealmDiscoverytest_by_default_b2c_should_skip_validation_and_instance_metadata(line 254)
tests/test_application.py::TestBrokertest_should_fallback_to_non_broker_when_using_b2c(line 827)
AAD Authority (standard/known hosts)
tests/test_authority.py::TestAuthority- Multiple tests using known hosts like login.microsoftonline.com
Multiple Authority Types
tests/test_authority.py::TestAuthorityInternalHelperUserRealmDiscoverytest_turning_off_instance_discovery_should_work_for_all_kinds_of_clouds(line 263)- Tests AAD, ADFS, B2C, and Private Cloud in one test
bgavrilMS
commented
Feb 17, 2026
@copilot Can you enhance the tests here for SHA256?
- Add SHA256-only tests for all authority types (AAD, B2C, CIAM, ADFS, OIDC, dSTS) - Add SHA1-only tests for all authority types - Verify single thumbprint behavior across all authority types - Increased test count from 13 to 23 tests - All tests passing Co-authored-by: bgavrilMS <12273384+bgavrilMS@users.noreply.github.com>
@copilot Can you enhance the tests here for SHA256?
Enhanced SHA256 tests with comprehensive coverage. Commit 40dfa91.
Added 10 new tests (13 → 23 total):
SHA256-only tests for all authority types:
- B2C, CIAM, ADFS, OIDC generic, dSTS
- Verifies SHA256 is used when it's the only thumbprint provided
SHA1-only tests for all authority types:
- B2C, CIAM, ADFS, OIDC generic, dSTS
- Verifies SHA1 is used when it's the only thumbprint provided
Tests now cover every authority type with:
- Single thumbprint (SHA1 only)
- Single thumbprint (SHA256 only)
- Both thumbprints (authority-based selection)
All 23 tests passing ✅
@pvaneck
pvaneck
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Verified with Azure/azure-sdk-for-python#44990 and am able to pass in thumbprint_sha256 for certificate-based authentication. Confirmed that PS256 and x5t#S256 are in the headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds support for supplying a SHA-256 certificate thumbprint (thumbprint_sha256) in client_credential, with authority-aware selection logic to choose SHA-256 (PS256) vs SHA-1 (RS256) when both thumbprints are available.
Changes:
- Extend
ConfidentialClientApplicationcertificate credential handling to acceptthumbprint_sha256and select SHA-1 vs SHA-256 based on authority type. - Add an internal
_is_oidcflag toAuthorityto distinguish generic OIDC/dSTS authorities from Entra/B2C/CIAM. - Expand unit tests to cover SHA1-only, SHA256-only, and dual-thumbprint selection across authority types.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
msal/application.py |
Accepts thumbprint_sha256, updates error messaging, and adds authority-based SHA1/SHA256 selection logic when building JWT assertions. |
msal/authority.py |
Introduces _is_oidc flag to classify generic OIDC/dSTS authorities vs Entra authorities. |
tests/test_optional_thumbprint.py |
Adds a comprehensive test matrix for SHA1/SHA256 thumbprint inputs and authority-based selection behavior. |
Comments suppressed due to low confidence (1)
tests/test_optional_thumbprint.py:34
- _setup_mocks() returns a plain Mock() authority without initializing boolean flags like _is_oidc/_is_b2c. Because Mock attributes are truthy when accessed, forgetting to explicitly set these in a test can silently flip authority classification (e.g., getattr(authority, '_is_oidc', False) returning a truthy Mock). Consider setting sensible defaults (e.g., _is_oidc=False, _is_b2c=False) in _setup_mocks() to make tests more robust and closer to real Authority instances.
def _setup_mocks(self, mock_authority_class, authority="https://login.microsoftonline.com/common"):
"""Helper to setup Authority mock"""
# Setup Authority mock
mock_authority = Mock()
mock_authority.is_adfs = "adfs" in authority.lower()
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Copilot
AI
Mar 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The client_credential docstring currently says that when thumbprint is absent and public_certificate is present, MSAL will automatically calculate a SHA-256 thumbprint instead. With the updated authority-based selection (e.g., ADFS/OIDC/dSTS falling back to SHA-1 when both thumbprints are available), this wording is misleading—please clarify that MSAL may calculate both SHA-1 and SHA-256 thumbprints from the certificate and then choose based on authority type.
Copilot
AI
Mar 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grammar: use "A SHA-256 thumbprint" (not "An SHA-256 thumbprint"). Consider aligning wording with the existing "thumbprint" description too for consistency.
Copilot
AI
Mar 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline "Authority classification" comment block mentions B2C/CIAM detection via authority._is_b2c (and not OIDC), but the actual selection logic only keys off authority.is_adfs and authority._is_oidc. To avoid future confusion, consider updating the comment to match the implemented conditions (and explain that B2C/CIAM are implicitly covered by "not ADFS and not OIDC").
pvaneck
commented
Apr 13, 2026
@bgavrilMS Any other blockers for this?
Uh oh!
There was an error while loading. Please reload this page.
Adds
thumbprint_sha256parameter to enable modern SHA256 certificate authentication alongside legacy SHA1 support.Changes
Core Implementation (
msal/application.py,msal/authority.py)thumbprint_sha256inclient_credentialdictionary_is_oidcflag to Authority class to distinguish OIDC generic authorities from B2C/CIAMuse_sha256 = not authority.is_adfs and not is_oidcAuthority Classification
authority.is_adfs→ SHA1authority._is_b2c(not OIDC) → SHA256authority._is_b2c(not OIDC) → SHA256authority._is_oidc→ SHA1/dstsv2, treated as OIDC → SHA1Tests (
tests/test_optional_thumbprint.py)Usage
Backward compatible - existing
thumbprint(SHA1) usage unchanged.Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.