docs: Guidance for higher-level SDKs to consume MSAL mTLS PoP#932
Closed
gladjohn wants to merge 2 commits into
Closed
docs: Guidance for higher-level SDKs to consume MSAL mTLS PoP#932gladjohn wants to merge 2 commits into
gladjohn wants to merge 2 commits into
Conversation
…roof End-to-end mTLS Proof-of-Possession for Managed Identity v2 on Windows Azure VMs with Credential Guard / KeyGuard. ## Core (msal package) - msal/windows_certificate.py: Python X509Certificate2 (NCRYPT_KEY_HANDLE wrapper) - msal/msi_v2.py: Full MSI v2 flow (KeyGuard key, CSR, attestation, issuecredential, mTLS token) - msal/managed_identity.py: Public API (acquire_token_for_client + mtls_proof_of_possession) ## Separate packages - msal-schannel-transport/: WinHTTP/SChannel for downstream mTLS (bypasses OpenSSL) - msal-key-attestation/: MAA attestation wrapper (DLL from NuGet: Microsoft.Azure.Security.KeyGuardAttestation v1.1.5) ## Dev app + docs - sample/devapp_msi_v2_mtls/app.py: Uses ONLY public MSAL API - docs/mTLS-PoP-Architecture-Decision.md: Why WinHTTP (OpenSSL CNG gap) ## E2E Results (MSIV2 VM, June 2026) - Token: mtls_pop ✓ | Binding: cnf.x5t#S256 MATCH ✓ | Downstream: HTTP 200 ✓ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explains how azure-identity and other SDKs can integrate with the MSI v2 mTLS Proof-of-Possession API exposed via PR #931. Covers: - Public API surface and return value contract - WindowsCertificate object usage - Step-by-step integration pattern (credential -> transport) - End-user DX goal (zero mTLS awareness) - .NET comparison - Transport architecture options - Future OpenSSL 3 CNG Provider path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| @property | ||
| def thumbprint_sha1(self) -> str: | ||
| """SHA-1 thumbprint of the certificate (hex uppercase).""" | ||
| return hashlib.sha1(self._cert_der).hexdigest().upper() |
| return 1 | ||
|
|
||
| if "error" in result: | ||
| print(f" ✗ Error: {result['error']}") |
|
|
||
| if "error" in result: | ||
| print(f" ✗ Error: {result['error']}") | ||
| print(f" {result.get('error_description', '')}") |
| token_type = result.get("token_type", "unknown") | ||
| expires_in = result.get("expires_in", 0) | ||
|
|
||
| print(f" ✓ access_token: {access_token[:30]}...") |
| expires_in = result.get("expires_in", 0) | ||
|
|
||
| print(f" ✓ access_token: {access_token[:30]}...") | ||
| print(f" ✓ token_type: {token_type}") |
| auth_header = f"{token_type} {access_token}" | ||
|
|
||
| print(f" URL: {downstream_url}") | ||
| print(f" Authorization: {token_type} <token>") |
| thumbprint = result.get("cert_thumbprint_sha256", "") | ||
|
|
||
| print("Token acquired successfully!") | ||
| print(f" token_type: {token_type}") |
|
|
||
| print("Token acquired successfully!") | ||
| print(f" token_type: {token_type}") | ||
| print(f" expires_in: {expires_in} seconds") |
| print("Token acquired successfully!") | ||
| print(f" token_type: {token_type}") | ||
| print(f" expires_in: {expires_in} seconds") | ||
| print(f" thumbprint: {thumbprint[:16]}..." if thumbprint else " thumbprint: (none)") |
| print(f"\n AKV response: HTTP {response.status_code}") | ||
| if response.status_code == 200: | ||
| body = response.json() | ||
| print(f" Secret retrieved: {E2E_SECRET_NAME}") |
Contributor
There was a problem hiding this comment.
Pull request overview
This pull request adds an end-to-end MSI v2 mTLS Proof-of-Possession (PoP) implementation for Windows (KeyGuard/Credential Guard), plus supporting transport + attestation packages, tests, samples, and SDK integration documentation so higher-level SDKs can consume the feature.
Changes:
- Introduces the MSI v2 flow (
msal.msi_v2) and a Windows-backed certificate handle type (WindowsCertificate) for mTLS PoP. - Adds two auxiliary packages:
msal-key-attestation(MAA/KeyGuard attestation JWT) andmsal-schannel-transport(WinHTTP/SChannel downstream mTLS). - Adds comprehensive unit tests, an opt-in Windows E2E test, samples, and integration guidance docs for SDK authors.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_msi_v2.py | Unit tests for cnf binding verification, IMDS helpers, and cert caching/gating behaviors. |
| tests/test_e2e_mtls_pop.py | Opt-in Windows E2E test: acquire mTLS PoP token and call Key Vault over mTLS via SChannel transport. |
| sample/msi_v2_sample.py | Runnable sample demonstrating MSI v2 token acquisition and header formation. |
| sample/MSI_V2_GUIDE.md | Setup/usage guide for MSI v2 + attestation package usage. |
| sample/devapp_msi_v2_mtls/app.py | Dev app demonstrating full public-API flow and downstream call using SchannelSession. |
| msal/windows_certificate.py | Adds WindowsCertificate object for platform-backed cert + non-exportable key handles. |
| msal/msi_v2.py | Adds the MSI v2 implementation: KeyGuard key mgmt, CSR, IMDS, cert binding, WinHTTP/SChannel token acquisition, and cert cache. |
| msal/managed_identity.py | Adds MSI v2 gating parameters + MsiV2Error and routes into msal.msi_v2.obtain_token. |
| msal/init.py | Exposes MsiV2Error and WindowsCertificate at package level. |
| msal-schannel-transport/pyproject.toml | Defines the downstream WinHTTP/SChannel transport package metadata. |
| msal-schannel-transport/msal_schannel_transport/session.py | Implements SchannelSession and response/error types for downstream mTLS calls. |
| msal-schannel-transport/msal_schannel_transport/init.py | Package exports for msal-schannel-transport. |
| msal-key-attestation/pyproject.toml | Defines the KeyGuard attestation package metadata and dynamic version config. |
| msal-key-attestation/msal_key_attestation/attestation.py | Implements AttestationClientLib.dll bindings + JWT caching and provider factory. |
| msal-key-attestation/MANIFEST.in | sdist manifest for the attestation package. |
| msal-key-attestation/LICENSE | License file for the attestation package. |
| docs/mTLS-PoP-Architecture-Decision.md | Rationale/decision record for WinHTTP/SChannel vs OpenSSL-provider approaches. |
| docs/MSI_V2_API.md | API reference for MSI v2 parameters, return contract, errors, and usage. |
| docs/Guidance-for-Higher-Level-SDKs-to-Consume-MSAL.md | Higher-level SDK integration guide (azure-identity/azure-core patterns). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+57
to
+60
| # Properties | ||
| cert.thumbprint_sha256 # str: base64url SHA-256 thumbprint | ||
| cert.pem # str: PEM-encoded public certificate | ||
| cert.subject # str: certificate subject |
Comment on lines
+311
to
+320
| # Downstream call | ||
| session = SchannelSession() | ||
| response = session.get( | ||
| "https://tokenbinding.vault.azure.net/secrets/boundsecret/?api-version=2015-06-01", | ||
| headers={ | ||
| "Authorization": f"{result['token_type']} {result['access_token']}", | ||
| "x-ms-tokenboundauth": "true", | ||
| }, | ||
| client_certificate=result["binding_certificate"], | ||
| ) |
Comment on lines
+6
to
+10
| name = "msal-schannel-transport" | ||
| version = "0.1.0" | ||
| description = "Windows SChannel/WinHTTP HTTP transport for mTLS with non-exportable keys" | ||
| readme = "README.md" | ||
| license = {text = "MIT"} |
Comment on lines
+8
to
+11
| description = "KeyGuard attestation support for MSAL Python MSI v2 (mTLS PoP). Provides Python bindings for AttestationClientLib.dll (Windows Credential Guard key attestation)." | ||
| readme = "README.md" | ||
| license = "MIT" | ||
| requires-python = ">=3.9" |
Comment on lines
+339
to
+344
| # Auto-discover attestation provider from msal-key-attestation | ||
| attestation_token_provider = None | ||
| try: | ||
| from msal_key_attestation import create_attestation_provider | ||
| attestation_token_provider = create_attestation_provider() | ||
| except ImportError as exc: |
Comment on lines
+34
to
+40
| Repository = "https://github.com/AzureAD/microsoft-authentication-library-for-python" | ||
|
|
||
| [tool.setuptools.dynamic] | ||
| version = {attr = "msal_key_attestation.__version__"} | ||
|
|
||
| [tool.setuptools.packages.find] | ||
| exclude = ["tests", "tests.*"] |
Comment on lines
+166
to
+175
| # Option A: Use msal-schannel-transport directly | ||
| from msal_schannel_transport import SchannelSession | ||
|
|
||
| session = SchannelSession() | ||
| response = session.get( | ||
| url, | ||
| headers=headers, | ||
| client_certificate=credential._binding_certificate, | ||
| ) | ||
| ``` |
Comment on lines
+5
to
+12
| """ | ||
| MSI v2 (IMDSv2) Managed Identity flow — Windows KeyGuard + SChannel mTLS PoP. | ||
|
|
||
| This module implements the MSI v2 token acquisition path using Windows native APIs | ||
| via ctypes: | ||
| - CNG/NCrypt: create/open a KeyGuard-protected per-boot RSA key (non-exportable) | ||
| - Minimal DER/PKCS#10: build a CSR signed with RSA-PSS/SHA256 | ||
| - IMDS: call getplatformmetadata + issuecredential |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds documentation explaining how higher-level SDKs (e.g., azure-identity, azure-sdk-for-python) can integrate with the MSI v2 mTLS Proof-of-Possession API.
Contents
Related