Add Linux Attested Managed Identity Flow documentation - #6149
Conversation
Document the Linux Attested Managed Identity flow using KMPP and MSAL.
There was a problem hiding this comment.
Pull request overview
Adds a new documentation page describing the Linux Attested Managed Identity (MSI v2) flow leveraging KMPP-based protected keys, MAA attestation, IMDS /issuecredential certificate issuance, and mTLS PoP token acquisition from the regional STS.
Changes:
- Introduces a new doc page for the Linux attested MSI v2 story.
- Adds a Mermaid sequence diagram capturing the end-to-end interactions (workload → MSAL → IMDS/KMPP/MAA → STS → AKV).
Comments suppressed due to low confidence (1)
docs/msi_v2/linux-attested-story.md:32
- For consistency with the other MSI v2 docs (which use the full IMDSv2 path), this should be
POST /metadata/identity/issuecredentialrather thanPOST issuecredential.
MSAL->>IMDS: POST issuecredential<br/>protected-key handle and attestation token
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c958606f-3471-4310-8715-83d1081964f5
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (4)
docs/msi_v2/msi_with_credential_design.md:177
- The
/issuecredentialrequest example makesattestation_tokenlook mandatory, but the platform metadata section indicates the attestation endpoint may only be present for attestable VMs. Document this field as optional (or explicitly state the flow requires an attestable VM).
{
"csr": "<Base64 CSR>",
"attestation_token": "<jwt>"
}
docs/msi_v2/msi_with_credential_design.md:346
- The token request in the PowerShell POC now always appends
token_type=mtls_popand always logs an "mTLS-PoP token". Ifattestation_endpointis missing (non-attestable VM), this can send an unsupported token_type and produce a misleading log message. Maketoken_typeconditional on having an attestation token (or fail fast).
$regional = "$regionalTokenUrl/$tenantId/oauth2/v2.0/token"
$bodyStr = "grant_type=client_credentials&scope=$([Uri]::EscapeDataString($Scope))&client_id=$clientId&token_type=mtls_pop"
$tokResp = Invoke-WebRequest -Uri $regional -Method POST -Body $bodyStr -Headers @{ 'Accept'='application/json'; 'Content-Type'='application/x-www-form-urlencoded' } -Certificate $miCert
Throw-IfError $tokResp 'token'
$tokenJson = $tokResp.Content | ConvertFrom-Json
}
Info "mTLS-PoP token acquired – expires_in = $($tokenJson.expires_in)s"
docs/msi_v2/msi_with_credential_design.md:61
- The technical Mermaid diagram currently shows attestation as unconditional and the token as always
mtls_pop, but thegetPlatformMetadataresponse is described later as returning the MAA endpoint only for attestable VMs. The diagram should reflect the optional attestation branch (and token type) to avoid implying the flow works the same on non-attestable VMs.
This issue also appears on line 174 of the same file.
MSAL ->> MAA: POST key attestation info
MAA -->> MSAL: attestation_token
MSAL ->> IMDS: POST /metadata/identity/issuecredential (CSR + attestation_token)
IMDS -->> MSAL: client_credential (x509), regional_token_url
MSAL ->> ESTS: POST /oauth2/v2.0/token (mTLS)
ESTS -->> MSAL: access_token (mtls_pop)
docs/msi_v2/msi_with_credential_design.md:317
- In the PowerShell POC,
$attTokenstays$nullwhenattestation_endpointis not returned. The updated code still serializesattestation_token: nullinto the/issuecredentialbody, which can break the request if the service expects the property to be omitted when not present.
This issue also appears on line 339 of the same file.
$bodyObj = @{ csr = $csrBase64; attestation_token = $attToken }
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c958606f-3471-4310-8715-83d1081964f5
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (4)
docs/msi_v2/msi_with_credential_design.md:73
- This bullet implies MSAL always performs key attestation and always sends an attestation token. In the current implementation, attestation is only performed when .WithAttestationSupport() configures an attestation provider; otherwise the request proceeds without attestation and the attestation_token field is omitted.
- On Windows, MSAL sources a non-exportable key from the KeyGuard KSP.
- The planned Linux implementation sources a non-exportable RSA-PSS key from KMPP through `libkmpp.so`.
- MSAL builds the CSR using the platform-backed key.
- MSAL obtains key attestation evidence from MAA and sends the CSR and attestation token to the MIRP.
- Linux KMPP key creation and CSR generation are currently a proof of concept; Linux attestation and the complete token flow are not yet implemented.
docs/msi_v2/msi_with_credential_design.md:177
- The /issuecredential request example makes attestation_token look required. In MSAL, CertificateRequestBody has JsonIgnore(WhenWritingNull) for AttestationToken, and the non-attested flow omits the property entirely when WithAttestationSupport() is not used. The sample should document this optionality to match the implementation and avoid suggesting clients send null/empty values.
```http
POST /metadata/identity/issuecredential?cid={CUID}&uaid={client_id}&api-version=2025-05-01 HTTP/1.1
Content-Type: application/json
{
"csr": "<Base64 CSR>",
"attestation_token": "<jwt>"
}
docs/msi_v2/msi_with_credential_design.md:318
- In the PowerShell sample, $attToken can remain $null (e.g., if $maaEp is empty/unavailable), but the request body always includes attestation_token = $attToken. ConvertTo-Json will serialize this as "attestation_token": null, which contradicts MSAL's behavior (it omits the property when null) and may cause the IMDS endpoint to reject the request. Build the JSON body to only include attestation_token when it has a value.
$issueUri = "http://169.254.169.254/metadata/identity/issuecredential?api-version=2025-05-01&cid=$cuid&uaid=$clientId"
$issueHdr = @{ Metadata = 'true'; 'X-ms-Client-Request-id' = [guid]::NewGuid() }
$bodyObj = @{ csr = $csrBase64; attestation_token = $attToken }
$issueResp = Invoke-WebRequest -Uri $issueUri -Method POST -Headers $issueHdr -Body ($bodyObj | ConvertTo-Json -Compress) -ContentType 'application/json'
docs/msi_v2/msi_with_credential_design.md:57
- The sequence diagram currently shows key attestation as unconditional, but MSAL supports a non-attested flow when .WithAttestationSupport() is not configured (see ImdsV2ManagedIdentitySource.GetAttestationJwtAsync which returns null and proceeds). The diagram should reflect attestation as optional and the attestation token as optional in the /issuecredential call.
This issue also appears in the following locations of the same file:
- line 69
- line 170
- line 315
MSAL ->> MAA: POST key attestation info
MAA -->> MSAL: attestation_token
MSAL ->> IMDS: POST /metadata/identity/issuecredential (CSR + attestation_token)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (7)
docs/msi_v2/msi_with_credential_design.md:62
- The diagram now states ESTS always returns an mtls_pop token. MSAL currently supports both mTLS PoP (token_type=mtls_pop) and mTLS bearer (token_type=bearer) depending on the requested authentication scheme, so the response here should not be mtls_pop-only.
MSAL ->> ESTS: POST /oauth2/v2.0/token (mTLS)
ESTS -->> MSAL: access_token (mtls_pop)
MSAL -->> App : AuthenticationResult (access_token + mtls_certificate)
docs/msi_v2/msi_with_credential_design.md:177
- This request body now makes attestation_token mandatory, but the client request model omits the property when null (non-attested flows). Marking this field as required may mislead readers and break the doc’s alignment with the implementation.
{
"csr": "<Base64 CSR>",
"attestation_token": "<jwt>"
}
docs/msi_v2/msi_with_credential_design.md:191
- The token request example now hard-codes token_type=mtls_pop, but MSAL also supports requesting token_type=bearer for the mTLS-bearer flow. The example should either show both options or explain when each is used.
grant_type=client_credentials
client_id=<UAID>
scope=https://management.azure.com/.default
token_type=mtls_pop
**docs/msi_v2/msi_with_credential_design.md:342**
* The PowerShell sample now always requests token_type=mtls_pop. MSAL supports both mtls_pop and bearer (mTLS bearer) depending on the chosen authentication scheme, so hard-coding mtls_pop here is misleading unless this sample is explicitly scoped to PoP-only. Consider either reintroducing a switch/parameter for bearer vs mtls_pop or clarifying in the doc that the sample is PoP-only.
$regional = "$regionalTokenUrl/$tenantId/oauth2/v2.0/token"
$bodyStr = "grant_type=client_credentials&scope=$([Uri]::EscapeDataString($Scope))&client_id=$clientId&token_type=mtls_pop"
$tokResp = Invoke-WebRequest -Uri $regional -Method POST -Body $bodyStr -Headers @{ 'Accept'='application/json'; 'Content-Type'='application/x-www-form-urlencoded' } -Certificate $miCert
Throw-IfError $tokResp 'token'
**docs/msi_v2/msi_with_credential_design.md:57**
* The sequence diagram now shows MAA attestation as unconditional. In the implementation, attestation is only performed for certain key types (e.g., KeyGuard); other key types proceed without calling MAA and without an attestation JWT. Consider restoring a conditional (alt/else) block here and making the attestation token optional in the subsequent /issuecredential call.
This issue also appears in the following locations of the same file:
- line 60
- line 174
- line 187
- line 339
MSAL ->> MAA: POST key attestation info
MAA -->> MSAL: attestation_token
MSAL ->> IMDS: POST /metadata/identity/issuecredential (CSR + attestation_token)
**docs/msi_v2/msi_with_credential_design.md:318**
* In the PowerShell sample, attestation_token is now always included in the /issuecredential request body. If $attToken is null/empty (e.g., when attestation is skipped), this sends a null token instead of omitting the property. Keeping the prior conditional addition better matches the request contract (omit when not present).
$issueUri = "http://169.254.169.254/metadata/identity/issuecredential?api-version=2025-05-01&cid=$cuid&uaid=$clientId"
$issueHdr = @{ Metadata = 'true'; 'X-ms-Client-Request-id' = [guid]::NewGuid() }
$bodyObj = @{ csr = $csrBase64; attestation_token = $attToken }
$issueResp = Invoke-WebRequest -Uri $issueUri -Method POST -Headers $issueHdr -Body ($bodyObj | ConvertTo-Json -Compress) -ContentType 'application/json'
**docs/msi_v2/linux-attested-story.md:43**
* This doc describes a "current implementation" with `libkmpp.so` interop and enclave-backed signing, but there is no corresponding KMPP/KeyIso implementation in this repo snapshot (no references to kmpp/keyiso/libkmpp outside docs). Unless the code lives elsewhere, this should be phrased as planned/future work or should link to the implementing PR/branch to avoid implying the feature exists in MSAL.NET today.
Current proof-of-concept scope
The current implementation covers:
- Native interoperability with
libkmpp.so. - Creating and opening an enclave-backed KMPP key.
- Reading the public key from the KMPP-generated self-signed certificate.
- Performing RSA-PSS/SHA-256 signing inside the enclave.
- Rejecting private-key export.
- Building the MSI V2 CSR with the KMPP-backed key.
</details>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
docs/msi_v2/msi_with_credential_design.md:302
- In the PowerShell sample,
$attTokencan remain$nullin non-mock mode whenattestation_endpointis missing (or when MAA doesn’t return a token). The script then always sendsattestation_token = $nulland later always requeststoken_type=mtls_pop, which will likely fail and is hard to diagnose. Consider failing fast with a clear error when an attestation token is required but not available.
if ($Mock) {
$attToken = "eyMock.Token"; Info "Mock attestation token generated."
} elseif ($maaEp) {
Info "Requesting attestation token from $maaEp …"
$maaResp = Invoke-WebRequest -Uri "$maaEp/attest/keyguard?api-version=2023-04-01-preview" -Method POST -Body (@{ AttestationInfo = '<bin>' } | ConvertTo-Json -Compress) -ContentType 'application/json'
Document the Linux Attested Managed Identity flow using KMPP and MSAL.