Skip to content

Commit 8acf865

Browse files
initial
1 parent 1f71ede commit 8acf865

File tree

18 files changed

+3113
-0
lines changed

18 files changed

+3113
-0
lines changed

msal-key-attestation/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# msal-key-attestation
2+
3+
KeyGuard attestation support for **MSAL Python** MSI v2 (mTLS Proof-of-Possession).
4+
5+
This package provides the `AttestationClientLib.dll` bindings for Windows
6+
Credential Guard / KeyGuard key attestation via Azure Attestation (MAA).
7+
8+
## Installation
9+
10+
```bash
11+
pip install msal msal-key-attestation
12+
```
13+
14+
## Prerequisites
15+
16+
- **Windows** with Credential Guard / KeyGuard enabled (Azure VM with VBS)
17+
- **AttestationClientLib.dll** — place it next to your application, or set
18+
`ATTESTATION_CLIENTLIB_PATH` environment variable to its full path.
19+
20+
## Usage
21+
22+
```python
23+
import msal, requests
24+
25+
client = msal.ManagedIdentityClient(
26+
msal.SystemAssignedManagedIdentity(),
27+
http_client=requests.Session(),
28+
)
29+
30+
# with_attestation_support=True auto-discovers msal-key-attestation
31+
result = client.acquire_token_for_client(
32+
resource="https://graph.microsoft.com",
33+
mtls_proof_of_possession=True,
34+
with_attestation_support=True,
35+
)
36+
37+
if "access_token" in result:
38+
print(f"Token type: {result['token_type']}") # mtls_pop
39+
print(f"Cert thumbprint: {result.get('cert_thumbprint_sha256', 'N/A')}")
40+
else:
41+
print(f"Error: {result.get('error_description', result)}")
42+
```
43+
44+
## How it works
45+
46+
1. MSAL Python's MSI v2 flow creates a KeyGuard-protected RSA key (via NCrypt)
47+
2. When `with_attestation_support=True`, MSAL auto-imports this package
48+
3. This package calls `AttestationClientLib.dll` to attest the key with MAA
49+
4. The attestation JWT is cached in-memory (~90% of its lifetime)
50+
5. MSAL sends the JWT + CSR to IMDS `/issuecredential`
51+
6. IMDS returns a short-lived certificate, which MSAL uses for mTLS token
52+
acquisition
53+
54+
## Architecture
55+
56+
```
57+
┌─────────────────────────────────────────┐
58+
│ msal (pip install msal) │
59+
│ │
60+
│ ManagedIdentityClient │
61+
│ └─ acquire_token_for_client() │
62+
│ mtls_proof_of_possession=True │
63+
│ with_attestation_support=True │
64+
│ │
65+
│ msal.msi_v2 (core flow) │
66+
│ - NCrypt KeyGuard key (ctypes) │
67+
│ - PKCS#10 CSR builder │
68+
│ - IMDS getplatformmetadata │
69+
│ - IMDS issuecredential │
70+
│ - Crypt32 cert binding │
71+
│ - WinHTTP/SChannel mTLS │
72+
│ - Certificate cache (in-memory) │
73+
└────────────────┬────────────────────────┘
74+
│ auto-discovers via import
75+
┌────────────────▼────────────────────────┐
76+
│ msal-key-attestation │
77+
│ (pip install msal-key-attestation) │
78+
│ │
79+
│ create_attestation_provider() │
80+
│ - AttestationClientLib.dll bindings │
81+
│ - MAA token cache (in-memory) │
82+
└─────────────────────────────────────────┘
83+
```
84+
85+
## Environment Variables
86+
87+
| Variable | Description |
88+
|---|---|
89+
| `ATTESTATION_CLIENTLIB_PATH` | Full path to `AttestationClientLib.dll` |
90+
| `MSAL_MSI_V2_ATTESTATION_CACHE` | `"0"` to disable MAA JWT caching |
91+
92+
## License
93+
94+
MIT
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
Metadata-Version: 2.4
2+
Name: msal-key-attestation
3+
Version: 0.1.0
4+
Summary: KeyGuard attestation support for MSAL Python MSI v2 (mTLS PoP). Provides AttestationClientLib.dll bindings for Windows Credential Guard key attestation.
5+
Home-page: https://github.com/AzureAD/microsoft-authentication-library-for-python
6+
Author: Microsoft Corporation
7+
Author-email: nugetaad@microsoft.com
8+
License: MIT
9+
Classifier: Development Status :: 3 - Alpha
10+
Classifier: Programming Language :: Python :: 3 :: Only
11+
Classifier: Programming Language :: Python :: 3
12+
Classifier: Programming Language :: Python :: 3.8
13+
Classifier: Programming Language :: Python :: 3.9
14+
Classifier: Programming Language :: Python :: 3.10
15+
Classifier: Programming Language :: Python :: 3.11
16+
Classifier: Programming Language :: Python :: 3.12
17+
Classifier: Programming Language :: Python :: 3.13
18+
Classifier: Programming Language :: Python :: 3.14
19+
Classifier: License :: OSI Approved :: MIT License
20+
Classifier: Operating System :: Microsoft :: Windows
21+
Requires-Python: >=3.8
22+
Description-Content-Type: text/markdown
23+
Requires-Dist: msal>=1.32.0
24+
25+
# msal-key-attestation
26+
27+
KeyGuard attestation support for **MSAL Python** MSI v2 (mTLS Proof-of-Possession).
28+
29+
This package provides the `AttestationClientLib.dll` bindings for Windows
30+
Credential Guard / KeyGuard key attestation via Azure Attestation (MAA).
31+
32+
## Installation
33+
34+
```bash
35+
pip install msal msal-key-attestation
36+
```
37+
38+
## Prerequisites
39+
40+
- **Windows** with Credential Guard / KeyGuard enabled (Azure VM with VBS)
41+
- **AttestationClientLib.dll** — place it next to your application, or set
42+
`ATTESTATION_CLIENTLIB_PATH` environment variable to its full path.
43+
44+
## Usage
45+
46+
```python
47+
import msal, requests
48+
49+
client = msal.ManagedIdentityClient(
50+
msal.SystemAssignedManagedIdentity(),
51+
http_client=requests.Session(),
52+
)
53+
54+
# with_attestation_support=True auto-discovers msal-key-attestation
55+
result = client.acquire_token_for_client(
56+
resource="https://graph.microsoft.com",
57+
mtls_proof_of_possession=True,
58+
with_attestation_support=True,
59+
)
60+
61+
if "access_token" in result:
62+
print(f"Token type: {result['token_type']}") # mtls_pop
63+
print(f"Cert thumbprint: {result.get('cert_thumbprint_sha256', 'N/A')}")
64+
else:
65+
print(f"Error: {result.get('error_description', result)}")
66+
```
67+
68+
## How it works
69+
70+
1. MSAL Python's MSI v2 flow creates a KeyGuard-protected RSA key (via NCrypt)
71+
2. When `with_attestation_support=True`, MSAL auto-imports this package
72+
3. This package calls `AttestationClientLib.dll` to attest the key with MAA
73+
4. The attestation JWT is cached in-memory (~90% of its lifetime)
74+
5. MSAL sends the JWT + CSR to IMDS `/issuecredential`
75+
6. IMDS returns a short-lived certificate, which MSAL uses for mTLS token
76+
acquisition
77+
78+
## Architecture
79+
80+
```
81+
┌─────────────────────────────────────────┐
82+
│ msal (pip install msal) │
83+
│ │
84+
│ ManagedIdentityClient │
85+
│ └─ acquire_token_for_client() │
86+
│ mtls_proof_of_possession=True │
87+
│ with_attestation_support=True │
88+
│ │
89+
│ msal.msi_v2 (core flow) │
90+
│ - NCrypt KeyGuard key (ctypes) │
91+
│ - PKCS#10 CSR builder │
92+
│ - IMDS getplatformmetadata │
93+
│ - IMDS issuecredential │
94+
│ - Crypt32 cert binding │
95+
│ - WinHTTP/SChannel mTLS │
96+
│ - Certificate cache (in-memory) │
97+
└────────────────┬────────────────────────┘
98+
│ auto-discovers via import
99+
┌────────────────▼────────────────────────┐
100+
│ msal-key-attestation │
101+
│ (pip install msal-key-attestation) │
102+
│ │
103+
│ create_attestation_provider() │
104+
│ - AttestationClientLib.dll bindings │
105+
│ - MAA token cache (in-memory) │
106+
└─────────────────────────────────────────┘
107+
```
108+
109+
## Environment Variables
110+
111+
| Variable | Description |
112+
|---|---|
113+
| `ATTESTATION_CLIENTLIB_PATH` | Full path to `AttestationClientLib.dll` |
114+
| `MSAL_MSI_V2_ATTESTATION_CACHE` | `"0"` to disable MAA JWT caching |
115+
116+
## License
117+
118+
MIT
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
README.md
2+
setup.cfg
3+
setup.py
4+
msal_key_attestation/__init__.py
5+
msal_key_attestation/attestation.py
6+
msal_key_attestation.egg-info/PKG-INFO
7+
msal_key_attestation.egg-info/SOURCES.txt
8+
msal_key_attestation.egg-info/dependency_links.txt
9+
msal_key_attestation.egg-info/requires.txt
10+
msal_key_attestation.egg-info/top_level.txt
11+
tests/__init__.py
12+
tests/test_attestation.py
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
msal>=1.32.0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
msal_key_attestation
2+
tests
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# All rights reserved.
3+
#
4+
# This code is licensed under the MIT License.
5+
"""
6+
msal-key-attestation — KeyGuard attestation support for MSAL Python MSI v2.
7+
8+
This package provides the ``create_attestation_provider()`` function that
9+
returns a callable suitable for the ``attestation_token_provider`` parameter
10+
in ``msal.msi_v2.obtain_token()``.
11+
12+
It loads the Windows-only ``AttestationClientLib.dll`` (Azure Attestation
13+
native library) via ctypes and exposes a high-level API that:
14+
15+
- Initializes the native attestation library
16+
- Calls ``AttestKeyGuardImportKey`` with the CNG key handle
17+
- Returns the attestation JWT
18+
- Caches the JWT in-memory until ~90 % of its lifetime
19+
20+
Usage::
21+
22+
from msal_key_attestation import create_attestation_provider
23+
24+
# Pass to MSAL's MSI v2 flow:
25+
result = client.acquire_token_for_client(
26+
resource="https://graph.microsoft.com",
27+
mtls_proof_of_possession=True,
28+
with_attestation_support=True, # auto-discovers this package
29+
)
30+
31+
# Or use the provider directly:
32+
provider = create_attestation_provider()
33+
jwt = provider(attestation_endpoint, key_handle_int, client_id)
34+
"""
35+
36+
__version__ = "0.1.0"
37+
38+
from .attestation import create_attestation_provider, get_attestation_jwt
39+
40+
__all__ = ["create_attestation_provider", "get_attestation_jwt"]

0 commit comments

Comments
 (0)