Skip to content

Commit 65f59bc

Browse files
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>
1 parent bd2eed1 commit 65f59bc

1 file changed

Lines changed: 50 additions & 13 deletions

File tree

docs/nsp_claims_design.md

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ Azure Redis Cache operates in a Backing resource VM/VMSS and uses MSAL with Mana
66

77
This document proposes a new `WithClientClaims()` API to support this scenario in a consistent, safe, and harmonized way across all MSAL auth flows.
88

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+
917
## Problem Statement
1018

1119
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*
3745

3846
### MSIv2 (IMDS v2)
3947

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.
4349

4450
## Proposed API: `WithClientClaims(string claimsJson)`
4551

@@ -61,13 +67,21 @@ Add `WithClientClaims(string claimsJson)` across the MSI, client credentials, an
6167
1. **Does not bypass the cache.** Tokens are cached and keyed on the claims value. Different claims values produce separate cache entries.
6268

6369
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
6781

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.
6983

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.
7185

7286
### Handling Dynamic Claims
7387

@@ -102,15 +116,36 @@ AuthenticationResult result = await miApp
102116

103117
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.
104118

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 |
125+
| MSIv2 (IMDS v2) | TBD | Blocked — IMDS team design pending | Q2/Q3 |
126+
127+
## E2E Testing Plan
128+
129+
E2E testing requires the Redis Cache team's help because this feature is gated in MIRP for Redis Cache delegated identities only.
106130

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.
108134

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
110136

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. |
141+
| 3 | MSIv1 claims param name | `claims` query parameter (OIDC standard), percent-encoded |
142+
| 4 | Rollout scope | MSIv1 first; MSIv2 and CCA follow once MSIv2 design is ready from IMDS team |
112143

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)*
114149

115150
## Related
116151

@@ -119,3 +154,5 @@ The per-request placement means the caller doesn't need to recreate the app when
119154
- `ClaimsHelper.GetMergedClaimsAndClientCapabilities()``ClaimsHelper.cs`
120155
- `ManagedIdentitySourceExtensions.SupportsClaimsAndCapabilities()``ManagedIdentitySourceExtensions.cs`
121156
- `CacheKeyFactory.GetAppTokenCacheItemKey()``CacheKeyFactory.cs`
157+
- POC implementation — PR #5999
158+

0 commit comments

Comments
 (0)