When using MSAL Python's MSI v2 flow, you acquire an mtls_pop token bound to a KeyGuard-protected certificate. Because KeyGuard keys are non-exportable, standard Python HTTP libraries like requests, httpx, or urllib3 (which use OpenSSL) cannot access the private key to perform mTLS.
You must use the mtls_http_request() helper. This function uses WinHTTP/SChannel (the Windows-native TLS stack) via ctypes, allowing it to access non-exportable CNG keys natively.
Requirements for successful mTLS resource calls:
- Header: You must include the
x-ms-tokenboundauth: true header (required by services like Azure Key Vault) to trigger the server to request the client certificate. - Protocol: Use HTTP/1.1. TLS renegotiation (required for client certificate requests) is forbidden in HTTP/2.
- TLS Version: Use TLS 1.2. TLS 1.3 uses post-handshake authentication which may not be fully supported by WinHTTP in this context.
from msal.msi_v2 import mtls_http_request
import base64
# 1. Acquire the mtls_pop token
result = client.acquire_token_for_client(
resource="https://vault.azure.net",
mtls_proof_of_possession=True,
with_attestation_support=True,
)
# 2. Prepare the certificate from the auth result
cert_der = base64.b64decode(result["cert_der_b64"])
# 3. Make the mTLS request using the helper
resp = mtls_http_request(
"GET",
"https://tokenbinding.vault.azure.net/secrets/boundsecret/?api-version=2015-06-01",
cert_der,
headers={
"Authorization": f"{result['token_type']} {result['access_token']}",
"Accept": "application/json",
"x-ms-tokenboundauth": "true",
},
)