Skip to content

Propagate KeyGuard attestation failures and bridge native MAA logs to the MSAL logger - #6081

Merged
Gladwin Johnson (gladjohn) merged 4 commits into
mainfrom
gladjohn/keyguard-attestation-error-and-logs
Jun 26, 2026
Merged

Propagate KeyGuard attestation failures and bridge native MAA logs to the MSAL logger#6081
Gladwin Johnson (gladjohn) merged 4 commits into
mainfrom
gladjohn/keyguard-attestation-error-and-logs

Conversation

@gladjohn

Copy link
Copy Markdown
Contributor

Summary

Fixes two defects in the IMDSv2 mTLS PoP + Credential Guard / KeyGuard attestation flow.

1. Propagate KeyGuard attestation failures (instead of sending an empty token to IMDS)

Previously, a failed attestation was mapped to null, which caused an empty / non-attested certificate request to be sent to IMDS — silently dropping the real failure (e.g. an MAA 400 PolicyEvaluationError / x-ms-azurevm-dbxvalidated=false deny).

  • WithAttestationSupport now throws MsalServiceException("attestation_failed") carrying Status / NativeErrorCode / reason instead of returning null. The failure originates from the MAA service, so it surfaces as a service exception.
  • ImdsV2ManagedIdentitySource.GetAttestationJwtAsync treats a null/empty token from a configured provider as a hard failure for KeyGuard keys (no silent non-attested fallback) and avoids double-wrapping MsalServiceException.
  • AttestationClient maps the native return code to a readable reason via AttestationErrors.Describe so the cause propagates.
  • Client-side preconditions (mtls_pop_requires_keyguard, credential_guard_requires_cng) remain MsalClientException.

2. Bridge native MAA logs into the MSAL logger

Native AttestationClientLib (MAA) logs were written only to System.Diagnostics.Trace and never reached the MSAL ILoggerAdapter, so failures were invisible in MSAL verbose logs.

  • AttestationLogger.CreateLoggerBridge forwards native log callbacks into the MSAL logger (Error/Warn/Info/DebugError/Warning/Info/Verbose), gated on IsLoggingEnabled, exception-safe, with a Trace fallback when no logger is supplied.
  • AttestationClient takes an optional ILoggerAdapter (keeping a strong reference to the bridge delegate for the native callback lifetime); PopKeyAttestor forwards the logger.

Validation

  • Verified end-to-end on a real Confidential VM: native MAA logs now flow through the MSAL logger, and the attested happy path works.
  • Added/updated unit tests in ImdsV2Tests covering attestation_failed propagation (NativeError / Exception / TokenEmpty / NotInitialized / empty-JWT, and null / empty / throwing providers) and the log bridge (level mapping, routing, gating, exception safety, null logger) plus AttestationErrors.Describe.
  • Microsoft.Identity.Test.Unit builds clean (net8.0 + netstandard2.0); ImdsV2Tests 99/99 pass.

Fixes #6079
Fixes #6080

… the MSAL logger

KeyGuard attestation failures were silently swallowed: a failed attestation
returned null, which caused an empty / non-attested certificate request to be
sent to IMDS instead of surfacing the failure. Separately, native
AttestationClientLib (MAA) logs were written only to System.Diagnostics.Trace
and never reached the MSAL ILoggerAdapter, so the real failure reason was
invisible in MSAL verbose logs.

Error propagation:
- WithAttestationSupport now throws MsalServiceException("attestation_failed")
  carrying Status / NativeErrorCode / reason instead of returning null. The
  failure originates from the MAA service, so it is surfaced as a service error.
- ImdsV2ManagedIdentitySource.GetAttestationJwtAsync treats a null/empty token
  from a configured provider as a hard failure for KeyGuard keys (no silent
  non-attested fallback) and avoids double-wrapping MsalServiceException.
- AttestationClient maps the native return code to a readable reason via
  AttestationErrors.Describe so the cause propagates to the caller.
- Client-side preconditions (mtls_pop_requires_keyguard,
  credential_guard_requires_cng) remain MsalClientException.

Log bridging:
- AttestationLogger.CreateLoggerBridge forwards native AttestationClientLib log
  callbacks into the MSAL ILoggerAdapter (Error/Warn/Info/Debug ->
  Error/Warning/Info/Verbose), gated on IsLoggingEnabled and exception-safe,
  with a Trace fallback when no logger is supplied.
- AttestationClient takes an optional ILoggerAdapter and keeps a strong
  reference to the bridge delegate for the native callback lifetime;
  PopKeyAttestor forwards the logger.

Tests:
- Added/updated unit tests in ImdsV2Tests covering attestation_failed
  propagation (NativeError/Exception/TokenEmpty/NotInitialized/empty-JWT,
  null/empty/throwing providers) and the log bridge (level mapping, routing,
  gating, exception safety, null logger) plus AttestationErrors.Describe.

Fixes #6079
Fixes #6080

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes two related defects in the IMDSv2 mTLS PoP + Credential Guard/KeyGuard attestation path by (1) ensuring attestation failures are surfaced to callers (instead of silently falling back to a non-attested IMDS request) and (2) forwarding native AttestationClientLib (MAA) logs into the MSAL ILoggerAdapter for diagnosability.

Changes:

  • Convert KeyGuard attestation failure cases into MsalServiceException("attestation_failed") and treat null/empty attestation tokens as hard failures for KeyGuard keys.
  • Add a native-to-MSAL logger bridge (AttestationLogger.CreateLoggerBridge) and plumb ILoggerAdapter through PopKeyAttestorAttestationClient.
  • Expand unit coverage for attestation failure propagation, native error reason mapping, and logger level mapping/routing.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs Updates/expands unit tests to validate attestation_failed propagation and native log bridging behavior.
src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs Enforces hard-fail behavior for empty attestation JWTs (KeyGuard) and avoids double-wrapping MsalServiceException.
src/client/Microsoft.Identity.Client.KeyAttestation/PopKeyAttestor.cs Passes MSAL logger through to the native attestation client to enable bridged logging.
src/client/Microsoft.Identity.Client.KeyAttestation/ManagedIdentityAttestationExtensions.cs Changes .WithAttestationSupport() to throw MsalServiceException("attestation_failed") on attestation failure instead of returning null.
src/client/Microsoft.Identity.Client.KeyAttestation/Attestation/AttestationLogger.cs Implements logger bridging and level mapping from native AttestationClientLib logs into MSAL logs.
src/client/Microsoft.Identity.Client.KeyAttestation/Attestation/AttestationClient.cs Adds optional logger support, keeps a strong reference to the native callback delegate, and maps native error codes to readable reasons.

Exclude OperationCanceledException (incl. TaskCanceledException) from the generic catch in GetAttestationJwtAsync so cancellation bubbles out unchanged instead of being masked as an attestation_failed service error. Adds a unit test. Addresses PR review feedback.
@Sheshagiri

Copy link
Copy Markdown

thanks a lot Gladwin Johnson (@gladjohn) for accomodating the changes. Would this also propage the logs from the underlying cpp code (AttestationClientLib) when we set AZURE_LOG_LEVEL=verbose?

@gladjohn

Copy link
Copy Markdown
Contributor Author

thanks a lot Gladwin Johnson (@gladjohn) for accomodating the changes. Would this also propage the logs from the underlying cpp code (AttestationClientLib) when we set AZURE_LOG_LEVEL=verbose?

yes, Sheshagiri Rao Mallipedhi (@Sheshagiri) it will

…ngelog

- AttestationLogger.CreateLoggerBridge: route native Error/Warning/Info to the
  scrubbed (non-PII) slot so MAA diagnostics stay visible, and route the most
  verbose output (Debug -> Verbose, plus unknown levels) through the PII slot so
  any richer payload fragments are redacted unless PII logging is enabled.
- Update routing test and add AttestationLogger_CreateLoggerBridge_VerboseLine_LogsAsPiiNotScrubbed.
- CHANGELOG: call out the fail-closed behavior change for KeyGuard attestation
  plus attestation_failed propagation and native MAA log bridging.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 24, 2026 00:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

…pport

WithAttestationSupport used IsNullOrEmpty, so a whitespace-only JWT from a
"Success" AttestationResult was returned as a valid token. Downstream IMDSv2
code (IsNullOrWhiteSpace) then threw a generic "returned no token" error,
discarding the richer Status/NativeErrorCode/Reason. Use IsNullOrWhiteSpace so
the failure is surfaced here with full detail. Adds regression test
MtlsPop_WithAttestationSupport_SuccessButWhitespaceJwt_ThrowsAttestationFailedWithReason.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Steinar Hjellvik (shjellvik) pushed a commit to equinor/osdu-csharp-client that referenced this pull request Aug 13, 2026
Updated
[Microsoft.Extensions.Configuration](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Configuration's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Configuration.Abstractions](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Configuration.Abstractions's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Configuration.Binder](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Configuration.Binder's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Configuration.EnvironmentVariables](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Configuration.EnvironmentVariables's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Configuration.Json](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Configuration.Json's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Configuration.UserSecrets](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Configuration.UserSecrets's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/dotnet)
from 10.0.9 to 10.0.10.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Extensions.Logging.Abstractions's
releases](https://github.com/dotnet/dotnet/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/dotnet/dotnet/commits).
</details>

Updated
[Microsoft.Identity.Client](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet)
from 4.84.2 to 4.87.0.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.Identity.Client's
releases](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/releases)._

## 4.87.0

## What's Changed

* Expose `MsalServiceException.ErrorCodesForLogging` for diagnostics in
#​6138
* Expose `WithOtelTagsEnricher` for managed identity requests in #​6144
* Forward OpenTelemetry tags enricher to the client-assertion callback
in #​6142
* Add client-side opaque-token log scrubber in #​6119
* Populate `ExecutionResult.Exception` for non-MSAL failures in #​6139
* Use PSS padding in KeyGuard liveness probe (CodeQL SM03799) in #​6141
* Remove managed identity support from `WithClaimsFromClient`
(confidential-client only) in #​6113
* Remove experimental features from client setup in #​6143
* Update Azure Arc managed identity API version from 2019-11-01 to
2020-06-01 in #​6130

**Full Changelog**:
AzureAD/microsoft-authentication-library-for-dotnet@4.86.1...4.87.0


## 4.86.1

### Bug Fixes
- Fixed the mTLS Proof-of-Possession token cache to key on the
certificate's full DER (`x5t#S256`) instead of only the public key,
preventing a stale token (and `AADSTS500181`) after a same-key
certificate renewal.
[#​6123](AzureAD/microsoft-authentication-library-for-dotnet#6123)
- Fell back to RS256 when a certificate's PSS signing operation is
rejected by `RSACryptoServiceProvider`, rebuilding the client assertion
so authentication can proceed.
[#​6126](AzureAD/microsoft-authentication-library-for-dotnet#6126)
- Detect and reject symbolic links in the Unix cache-file write path
(lstat pre-check plus `O_NOFOLLOW`), closing a TOCTOU window.
[#​6115](AzureAD/microsoft-authentication-library-for-dotnet#6115)
- Corrected misleading "region required" error messages and doc comments
in the mTLS PoP flow.
[#​6127](AzureAD/microsoft-authentication-library-for-dotnet#6127)



## 4.86.0

## What's Changed
* Propagate KeyGuard attestation failures and bridge native MAA logs to
the MSAL logger by @​gladjohn in
AzureAD/microsoft-authentication-library-for-dotnet#6081
* Token Binding Demo Helper by @​gladjohn in
AzureAD/microsoft-authentication-library-for-dotnet#6097
* Include ManagedIdentitySource in managed identity error messages and
request-failure logs by @​Robbie-Microsoft in
AzureAD/microsoft-authentication-library-for-dotnet#6101
* Remove Mooncake (AzureChinaCloud) lab client from WAM dev apps by
@​RyAuld in
AzureAD/microsoft-authentication-library-for-dotnet#6103
* Surface token-acquisition failure metadata on MsalException by
@​neha-bhargava in
AzureAD/microsoft-authentication-library-for-dotnet#6096
* Skip IMDS lookup for explicit regions by @​4gust in
AzureAD/microsoft-authentication-library-for-dotnet#6092
* Fix region failure-metadata test broken by explicit-region IMDS skip
by @​neha-bhargava in
AzureAD/microsoft-authentication-library-for-dotnet#6105


**Full Changelog**:
AzureAD/microsoft-authentication-library-for-dotnet@4.85.2...4.86.0

## 4.85.2

## What's Changed

* Delegate IMDSv2 mTLS-PoP token leg to internal TokenClient exchange
(MSIv2 WithClaimsFromClient) by @​Robbie-Microsoft in
AzureAD/microsoft-authentication-library-for-dotnet#6070
* Enforce mTLS PoP minimum binding strength for Managed Identity (#​6049
Phase 2) by @​Robbie-Microsoft in
AzureAD/microsoft-authentication-library-for-dotnet#6059
* Add refresh token cache partitioning support by @​iNinja in
AzureAD/microsoft-authentication-library-for-dotnet#6077
* Detach ImdsV2ManagedIdentitySource from AbstractManagedIdentity
(refused-bequest cleanup) by @​Robbie-Microsoft in
AzureAD/microsoft-authentication-library-for-dotnet#6089


**Full Changelog**:
AzureAD/microsoft-authentication-library-for-dotnet@4.85.1...4.85.2

## 4.85.1

## What's Changed
* Migrate OBO tests from old lab to ID4SLAB1 by @​RyAuld in
AzureAD/microsoft-authentication-library-for-dotnet#6021
* Mark regional SNI mTLS PoP test inconclusive on AAD test-slice Bearer
downgrade by @​neha-bhargava in
AzureAD/microsoft-authentication-library-for-dotnet#6084
* Expose canonical tag names per-metric by @​ssmelov in
AzureAD/microsoft-authentication-library-for-dotnet#6076


**Full Changelog**:
AzureAD/microsoft-authentication-library-for-dotnet@4.85.0...4.85.1

## 4.85.0

## What's Changed
* Fix proactive token refresh bypassing cancellation, leading to
unbounded semaphore wait by @​jayesh-a-shah in
AzureAD/microsoft-authentication-library-for-dotnet#6054
* Add GovFr, GovDe, GovSg to AzureCloudInstance enum by @​bgavrilMS in
AzureAD/microsoft-authentication-library-for-dotnet#6023
* Take home account from request, if not available elsewhere. by @​yowl
in
AzureAD/microsoft-authentication-library-for-dotnet#5657
* Validate Azure region format to prevent region poisoning (fixes
#​6060) by @​Robbie-Microsoft in
AzureAD/microsoft-authentication-library-for-dotnet#6061
* Promote MsalServiceException.SubError to public by @​neha-bhargava in
AzureAD/microsoft-authentication-library-for-dotnet#6063
* Migrate region discovery to IMDS /compute JSON endpoint (#​6039) by
@​Robbie-Microsoft in
AzureAD/microsoft-authentication-library-for-dotnet#6057
* fix: Service Fabric MI sends principalId for ObjectId; reject
ClientId/ResourceId (mirror of #​6066) by @​neha-bhargava in
AzureAD/microsoft-authentication-library-for-dotnet#6069
* Exclude caller SDK telemetry from access token cache keys by
@​bgavrilMS in
AzureAD/microsoft-authentication-library-for-dotnet#6074
* Add MSAL.NET telemetry enrichment by @​ssmelov in
AzureAD/microsoft-authentication-library-for-dotnet#6071

## New Contributors
* @​jayesh-a-shah made their first contribution in
AzureAD/microsoft-authentication-library-for-dotnet#6054
* @​yowl made their first contribution in
AzureAD/microsoft-authentication-library-for-dotnet#5657

**Full Changelog**:
AzureAD/microsoft-authentication-library-for-dotnet@4.84.2...4.85.0

Commits viewable in [compare
view](AzureAD/microsoft-authentication-library-for-dotnet@4.84.2...4.87.0).
</details>

Updated [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest)
from 18.6.0 to 18.8.1.

<details>
<summary>Release notes</summary>

_Sourced from [Microsoft.NET.Test.Sdk's
releases](https://github.com/microsoft/vstest/releases)._

## 18.8.1

## What's Changed
* Fix protocol negotiation timeout when STJ reflection is disabled
(18.8.1) by @​nohwnd in microsoft/vstest#16281


**Full Changelog**:
microsoft/vstest@v18.8.0...v18.8.1

## 18.8.0

## What's Changed
* Migrate from Newtonsoft.Json to System.Text.Json / Jsonite (merge to
main) by @​nohwnd in microsoft/vstest#15687
- For more detail refer to
https://devblogs.microsoft.com/dotnet/vs-test-is-removing-its-newtonsoft-json-dependency/
* Create source-only filter package by @​Youssef1313 in
microsoft/vstest#15638
* Add ARM64 msdia140.dll support to test platform packages by @​nohwnd
in microsoft/vstest#15692
* Fix mutex cleanup crash on macOS/Linux by @​nohwnd in
microsoft/vstest#15684
* Restrict artifact temp directory permissions on Unix by @​nohwnd in
microsoft/vstest#15729
* Add support for filtering uncategorized tests with TestCategory=None
by @​Evangelink in microsoft/vstest#15727
* Fix SCI binding failure in DTA hosts (main) by @​nohwnd in
microsoft/vstest#15724
* Fix HTML logger parallel file collision by @​nohwnd in
microsoft/vstest#15435
* Improve error message when testhost cannot be found by @​nohwnd in
microsoft/vstest#16053
* Fix HTML logger exception on invalid XML chars in test display names
by @​nohwnd in microsoft/vstest#16051

**Full Changelog**:
microsoft/vstest@v18.7.0...v18.8.0

## 18.7.0

## What's Changed
* Add ARM64 msdia140.dll support to test platform packages by
@​jamesmcroft in microsoft/vstest#15689
* Update System.Memory from 4.5.5 to 4.6.3 by @​nohwnd in
microsoft/vstest#15706

## New Contributors
* @​jamesmcroft made their first contribution in
microsoft/vstest#15689

**Full Changelog**:
microsoft/vstest@v18.6.0...v18.7.0

Commits viewable in [compare
view](microsoft/vstest@v18.6.0...v18.8.1).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants