You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: update NSP claims design doc with resolved questions, ETAs, scope restriction, and MSIv1 claim allowlist
- Clarify CCA transport: WithClientClaims sends claims as ESTS request body parameter,
NOT embedded in the client assertion JWT. This resolves Bogdan's concern.
- Add Scope section: MIRP-gated, Redis Cache only, delegated identities only initially.
- Add MSIv1 claim restriction: only xms_az_nwperimid is accepted; MSAL validates upfront
to avoid opaque HTTP 400 from IMDS.
- Add ETAs table: CCA done; MSIv1 (Raghu) canary by June 30; MSIv2 blocked on IMDS design.
- Add E2E testing plan: Redis Cache team's help needed for MSI; existing test tenant for CCA.
- Move resolved questions from Open Questions to a Resolved Questions table.
- Update remaining open questions to only the two still unresolved items.
- Add link to POC implementation PR #5999.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: docs/nsp_claims_design.md
+50-13Lines changed: 50 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,6 +6,14 @@ Azure Redis Cache operates in a Backing resource VM/VMSS and uses MSAL with Mana
6
6
7
7
This document proposes a new `WithClientClaims()` API to support this scenario in a consistent, safe, and harmonized way across all MSAL auth flows.
8
8
9
+
## Scope and Initial Rollout
10
+
11
+
> **Important:** This feature is initially scoped to a specific scenario. The IMDS/MIRP team will gate it in the service layer.
12
+
13
+
-**First consumer**: Azure Redis Cache
14
+
-**Initial service-side scope**: MIRP will enable this only for **delegated identities** in Redis Cache
15
+
-**General availability**: Depends on IMDS/MIRP team's rollout plan; do not assume this is a general-purpose NSP claims mechanism at this time
16
+
9
17
## Problem Statement
10
18
11
19
The existing `WithClaims()` API in MSAL is designed for **server-issued** claims challenges — situations where ESTS or a downstream web API rejects a token and asks the client to re-authenticate with specific claims (e.g., CAE, MFA step-up). Its behavior is:
@@ -37,9 +45,7 @@ For the NSP scenario, claims need to be sent to IMDS as a query parameter **and*
37
45
38
46
### MSIv2 (IMDS v2)
39
47
40
-
MSIv2 uses a different protocol from MSIv1. It acquires an mTLS binding certificate from IMDS, then makes a POST directly to an ESTS token endpoint (`/oauth2/v2.0/token`). Claims are not forwarded in that POST request today.
41
-
42
-
> **Open question for IMDS team**: Does the MSIv2 ESTS endpoint accept a `claims` body parameter the same way the standard ESTS token endpoint does?
48
+
MSIv2 uses a different protocol from MSIv1. It acquires an mTLS binding certificate from IMDS, then makes a POST directly to an ESTS token endpoint (`/oauth2/v2.0/token`). The MSIv2 design for `WithClientClaims` is not finalized — the IMDS team is still working on it. See the **ETAs** section.
@@ -61,13 +67,21 @@ Add `WithClientClaims(string claimsJson)` across the MSI, client credentials, an
61
67
1.**Does not bypass the cache.** Tokens are cached and keyed on the claims value. Different claims values produce separate cache entries.
62
68
63
69
2.**Transport-agnostic API.** MSAL routes the claims to the correct location per flow:
64
-
- MSIv1: query parameter to IMDS
65
-
- MSIv2: body parameter in the ESTS POST request
66
-
- Cert-based / FIC: body parameter sent to ESTS
70
+
- MSIv1: percent-encoded query parameter (`claims=...`) to IMDS
71
+
- MSIv2: body parameter in the ESTS POST request *(design pending IMDS team confirmation)*
72
+
- Cert-based / FIC: `claims` body parameter sent to ESTS — **not** embedded in the client assertion JWT
73
+
74
+
3.**CCA: claims go in the request body, not the JWT.** For confidential client flows, `WithClientClaims` sends the NSP claim as a standard ESTS `claims` body parameter. It is **not** placed inside the signed client assertion JWT. The existing `WithExtraClientAssertionClaims` API (separate, unrelated) handles the JWT-embedding path. These two APIs are distinct and serve different purposes.
75
+
76
+
4.**MSAL owns the JSON merge.** If a server-issued claims challenge (e.g., CAE) arrives while `WithClientClaims` is set, MSAL merges the two claims objects using the existing `ClaimsHelper` infrastructure. This infrastructure already performs JSON merging for cert-based flows today.
77
+
78
+
5.**Stable claims only.** Callers should avoid dynamic values (timestamps, nonces) in the claims string — each unique claims value creates a distinct cache entry, and frequently changing values will create an unbounded cache.
79
+
80
+
### MSIv1 claim restriction
67
81
68
-
3.**MSAL owns the JSON merge.** If a server-issued claims challenge (e.g., CAE) arrives while `WithClientClaims` is set, MSAL merges the two claims objects using the existing `ClaimsHelper` infrastructure. This infrastructure already performs JSON merging for cert-based flows today.
82
+
MSIv1 (IMDS v1) only accepts a single custom claim: `xms_az_nwperimid`. Any other claim key in the JSON causes IMDS to return HTTP 400 Bad Request with no diagnostic detail.
69
83
70
-
4.**Stable claims only.** Callers should avoid dynamic values (timestamps, nonces) in the claims string — each unique claims value creates a distinct cache entry, and frequently changing values will create an unbounded cache.
84
+
To avoid this silent failure, MSAL validates the claims JSON upfront for MSIv1 requests. If any top-level key other than `xms_az_nwperimid` is present, MSAL throws `MsalClientException(MsalError.InvalidRequest)` before making any network call.
71
85
72
86
### Handling Dynamic Claims
73
87
@@ -102,15 +116,36 @@ AuthenticationResult result = await miApp
102
116
103
117
The per-request placement means the caller doesn't need to recreate the app when claims update — a new request with new claims produces a new cache entry automatically.
104
118
105
-
## Open Questions
119
+
## ETAs
120
+
121
+
| Flow | Owner | Status | ETA |
122
+
|------|-------|--------|-----|
123
+
| CCA (cert-based / FIC) | Robbie | ✅ Done — included in POC PR | — |
124
+
| MSIv1 (IMDS v1) | Raghu | In progress | Canary by June 30 |
E2E testing requires the Redis Cache team's help because this feature is gated in MIRP for Redis Cache delegated identities only.
106
130
107
-
1.**API shape**: Is `WithClientClaims` the right name and signature for all teams involved?
131
+
-**MSI flows**: Requires a test VM with Managed Identity configured inside an NSP. The Redis Cache team will coordinate access to a suitable test environment.
132
+
-**CCA flow**: Can be tested with an existing MSAL test tenant app registration. Verify that the `claims` body parameter is forwarded to ESTS and the returned token contains `xms_az_nwperimid`.
133
+
-**Status**: Nidhi is confirming test environment availability with the internal team and the Redis Cache team.
108
134
109
-
2.**MSIv2 protocol***(for IMDS team)*: Does the MSIv2 ESTS endpoint accept a `claims` body parameter? This cannot be confirmed from the MSAL code alone.
135
+
## Resolved Questions
110
136
111
-
3.**MSIv1 claims param name**: Should the NSP claim be sent as `claims` (OIDC standard) or under a different query parameter name specific to IMDS?
137
+
| # | Question | Resolution |
138
+
|---|----------|------------|
139
+
| 1 | Is `WithClientClaims` the right name? | Yes — agreed across teams |
140
+
| 2 | CCA: request body or client assertion JWT? |**Request body only.** Claims are sent as the ESTS `claims` body parameter. They are not embedded in the signed client assertion JWT. |
| 4 | Rollout scope | MSIv1 first; MSIv2 and CCA follow once MSIv2 design is ready from IMDS team |
112
143
113
-
4.**Rollout scope**: Implement for all flows in one PR, or start with MSIv1 and extend MSIv2/cert/FIC in follow-ups?
144
+
## Open Questions
145
+
146
+
1.**MSIv2 protocol***(for IMDS team)*: What additional changes are needed in the `/issuecredential` request body to signal that custom claims are in use? The IMDS team is designing this; MSAL implementation will follow once the contract is confirmed.
147
+
148
+
2.**E2E test environment**: What test VM and tenant are available? *(Nidhi coordinating with internal team and Redis Cache)*
114
149
115
150
## Related
116
151
@@ -119,3 +154,5 @@ The per-request placement means the caller doesn't need to recreate the app when
0 commit comments