diff --git a/docs/msi_v2/msal-bound-token-api-spec.md b/docs/msi_v2/msal-bound-token-api-spec.md
new file mode 100644
index 0000000000..4e96dbfdb1
--- /dev/null
+++ b/docs/msi_v2/msal-bound-token-api-spec.md
@@ -0,0 +1,634 @@
+# MSAL.NET API Spec: `WithMtlsPopFallback()` + `MtlsPopOptions` — Bound Token Acquisition with Fallback
+
+**Status:** Draft
+**Date:** April 30, 2026
+**Applies to:** `Microsoft.Identity.Client` (MSAL.NET)
+**Related PR:** [AzureAD/microsoft-identity-web#3773](https://github.com/AzureAD/microsoft-identity-web/pull/3773)
+
+---
+
+## 1. Problem Statement
+
+### Current State (PR #3773)
+
+IdWeb currently calls MSAL's low-level APIs explicitly:
+
+```csharp
+// Pure MSI path (TokenAcquisition.cs)
+miBuilder.WithMtlsProofOfPossession()
+ .WithAttestationSupport();
+
+// FIC path (ManagedIdentityClientAssertion.cs)
+miBuilder.WithMtlsProofOfPossession()
+ .WithAttestationSupport();
+```
+
+This requires IdWeb (a higher-level SDK) to:
+1. **Orchestrate fallback policy** — IdWeb hard-codes the binding approach with no fallback if attestation fails at runtime.
+2. **Take a package dependency** on `Microsoft.Identity.Client.KeyAttestation` (this dependency remains, but the fallback orchestration moves to MSAL).
+3. **Couple to low-level mechanism details** — IdWeb explicitly chains `WithMtlsProofOfPossession().WithAttestationSupport()`, prescribing the exact binding strategy rather than declaring intent.
+
+> **Note:** The `Microsoft.Identity.Client.KeyAttestation` package dependency and the `WithAttestationSupport()` call remain in IdWeb because that call brings in the native Credential Guard DLL. This proposal moves the **fallback orchestration** into MSAL, not the package dependency itself.
+
+### Current MSAL Behavior When Things Go Wrong
+
+| Scenario | Current Behavior | Desired Behavior |
+|----------|-----------------|------------------|
+| KeyGuard key + attestation succeeds | ✅ Works | ✅ Same |
+| KeyGuard key + attestation provider not configured | ✅ Non-attested flow (returns null) | ✅ Same (not a fallback scenario) |
+| KeyGuard key + attestation **fails** (provider throws) | ❌ **Throws `attestation_failed`** | 🔄 Fall back to non-attested flow |
+| KeyGuard key + key is not RSACng | ❌ **Throws `credential_guard_requires_cng`** | 🔄 Fall back to non-attested flow |
+| Non-KeyGuard key (Hardware/InMemory) | ❌ **Throws `mtls_pop_requires_keyguard`** | 🔄 Proceed with non-attested mTLS PoP |
+| mTLS PoP not supported (IMDSv1 host) | ❌ Throws | ❌ Throws (correct — no fallback to bearer) |
+| Non-Windows or NET462 | ❌ Throws | ❌ Throws (platform unsupported) |
+
+### Design Principle
+
+> **IdWeb needs a bound token. What MSAL does internally to get it should not be visible to IdWeb.**
+>
+> MSAL should try attested flow first, and if that fails, fall back to non-attested flow. The fallback is transparent to the caller.
+
+---
+
+## 2. Proposed API
+
+Three-level API surface — from simplest to most configurable:
+
+| API | Use Case | Fallback? |
+|-----|----------|-----------|
+| `WithMtlsPopFallback()` | **IdWeb / higher-level SDKs** — recommended | ✅ Yes |
+| `WithMtlsProofOfPossession(MtlsPopOptions)` | Advanced callers needing fine-grained control | Configurable via options |
+| `WithMtlsProofOfPossession()` | Existing strict API — no fallback | ❌ No |
+
+### 2.1 New Convenience Method: `WithMtlsPopFallback()`
+
+**Package:** `Microsoft.Identity.Client` (core package)
+**Target class:** `AcquireTokenForManagedIdentityParameterBuilder`
+
+```csharp
+namespace Microsoft.Identity.Client
+{
+ public static class ManagedIdentityPopExtensions
+ {
+ ///
+ /// Requests an mTLS-bound (Proof-of-Possession) token with automatic fallback.
+ /// MSAL will first attempt the attested binding flow (if WithAttestationSupport()
+ /// was called). If attestation fails, MSAL silently falls back to the
+ /// non-attested mTLS PoP flow instead of throwing.
+ ///
+ /// This is the recommended API for higher-level SDKs (e.g., Microsoft.Identity.Web)
+ /// that need a bound token without coupling to specific binding mechanisms.
+ ///
+ /// Equivalent to:
+ /// WithMtlsProofOfPossession(new MtlsPopOptions { EnableFallback = true })
+ ///
+ /// The AcquireTokenForManagedIdentityParameterBuilder instance.
+ /// The builder to chain .With methods.
+ public static AcquireTokenForManagedIdentityParameterBuilder WithMtlsPopFallback(
+ this AcquireTokenForManagedIdentityParameterBuilder builder)
+ {
+ return builder.WithMtlsProofOfPossession(
+ new MtlsPopOptions { EnableFallback = true });
+ }
+ }
+}
+```
+
+### 2.2 New Overload: `WithMtlsProofOfPossession(MtlsPopOptions)`
+
+```csharp
+namespace Microsoft.Identity.Client
+{
+ public static class ManagedIdentityPopExtensions
+ {
+ ///
+ /// Enables mTLS Proof-of-Possession with configurable behavior.
+ /// Use to control fallback and other settings.
+ ///
+ /// The AcquireTokenForManagedIdentityParameterBuilder instance.
+ /// Options controlling mTLS PoP behavior.
+ /// The builder to chain .With methods.
+ ///
+ /// Thrown when is .
+ ///
+ public static AcquireTokenForManagedIdentityParameterBuilder WithMtlsProofOfPossession(
+ this AcquireTokenForManagedIdentityParameterBuilder builder,
+ MtlsPopOptions options)
+ {
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+#if NET462
+ throw new MsalClientException(
+ MsalError.MtlsNotSupportedForManagedIdentity,
+ MsalErrorMessage.MtlsNotSupportedForManagedIdentityMessage);
+#else
+ if (!DesktopOsHelper.IsWindows())
+ {
+ throw new MsalClientException(
+ MsalError.MtlsNotSupportedForManagedIdentity,
+ MsalErrorMessage.MtlsNotSupportedForNonWindowsMessage);
+ }
+
+ builder.CommonParameters.IsMtlsPopRequested = true;
+ builder.CommonParameters.IsBoundTokenFallbackEnabled = options.EnableFallback;
+ return builder;
+#endif
+ }
+
+ // Existing API — unchanged, strict, no fallback.
+ // The new overload mirrors the same NET462/non-Windows constraints.
+ public static AcquireTokenForManagedIdentityParameterBuilder WithMtlsProofOfPossession(
+ this AcquireTokenForManagedIdentityParameterBuilder builder) { /* unchanged */ }
+ }
+}
+```
+
+### 2.3 New Options Class: `MtlsPopOptions`
+
+```csharp
+namespace Microsoft.Identity.Client
+{
+ ///
+ /// Options for configuring mTLS Proof-of-Possession token acquisition behavior.
+ ///
+ public class MtlsPopOptions
+ {
+ ///
+ /// When true, MSAL will attempt attested binding first, and if attestation
+ /// fails at runtime (attestation provider throws, or key is not RSACng for Credential Guard),
+ /// silently fall back to non-attested mTLS PoP binding instead of throwing.
+ /// Also allows non-KeyGuard key types (Hardware, InMemory) to proceed with
+ /// non-attested binding instead of throwing `mtls_pop_requires_keyguard`.
+ ///
+ /// Note: When the attestation provider is not configured (WithAttestationSupport()
+ /// not called), MSAL already proceeds with non-attested flow in both strict
+ /// and fallback modes — this is existing behavior, not a fallback scenario.
+ ///
+ /// When false (default), MSAL uses strict mode: attestation provider
+ /// exceptions and non-KeyGuard keys result in exceptions.
+ ///
+ /// Default: false
+ ///
+ public bool EnableFallback { get; set; }
+ }
+}
+```
+
+### 2.4 `WithAttestationSupport()` — Unchanged
+
+`WithAttestationSupport()` stays in `Microsoft.Identity.Client.KeyAttestation` package. It **still needs to be called by IdWeb** because:
+1. It brings in the **native Credential Guard DLL** via the KeyAttestation package.
+2. It **registers** the attestation token provider delegate on the builder.
+3. Without it, the fallback chain simply skips attestation and proceeds to non-attested binding.
+
+```csharp
+// KeyAttestation package — NO changes to this API
+namespace Microsoft.Identity.Client.KeyAttestation
+{
+ public static class ManagedIdentityAttestationExtensions
+ {
+ ///
+ /// Registers the Credential Guard attestation provider.
+ /// Used with WithMtlsPopFallback() or WithMtlsProofOfPossession() to enable
+ /// attested mTLS PoP flows. Order of calls does not matter — both set
+ /// independent builder state.
+ ///
+ /// When used with WithMtlsPopFallback():
+ /// - Attestation is attempted first
+ /// - On failure, MSAL falls back to non-attested flow
+ ///
+ /// When used with WithMtlsProofOfPossession() (no options):
+ /// - Attestation failure throws an exception (strict mode)
+ ///
+ public static AcquireTokenForManagedIdentityParameterBuilder WithAttestationSupport(
+ this AcquireTokenForManagedIdentityParameterBuilder builder)
+ {
+ // Unchanged — sets AttestationTokenProvider delegate
+ }
+ }
+}
+```
+
+---
+
+## 3. Internal Plumbing
+
+### 3.1 New Property on Parameter Classes
+
+Add to `AcquireTokenCommonParameters`:
+
+```csharp
+internal class AcquireTokenCommonParameters
+{
+ // Existing
+ public bool IsMtlsPopRequested { get; set; }
+ public Func>
+ AttestationTokenProvider { get; set; }
+
+ // NEW
+ public bool IsBoundTokenFallbackEnabled { get; set; }
+}
+```
+
+Add to `AcquireTokenForManagedIdentityParameters`:
+
+```csharp
+internal class AcquireTokenForManagedIdentityParameters
+{
+ public bool IsMtlsPopRequested { get; set; }
+ public bool IsBoundTokenFallbackEnabled { get; set; } // NEW
+ public Func>
+ AttestationTokenProvider { get; set; }
+}
+```
+
+### 3.2 Propagation in `ApplyMtlsPopAndAttestation()`
+
+The existing `ApplyMtlsPopAndAttestation()` in `AcquireTokenForManagedIdentityParameterBuilder` copies `IsMtlsPopRequested` and `AttestationTokenProvider` from common params to MI params. It must also copy the new flag:
+
+```csharp
+private static void ApplyMtlsPopAndAttestation(
+ AcquireTokenCommonParameters acquireTokenCommonParameters,
+ AcquireTokenForManagedIdentityParameters acquireTokenForManagedIdentityParameters)
+{
+ acquireTokenForManagedIdentityParameters.IsMtlsPopRequested =
+ acquireTokenCommonParameters.IsMtlsPopRequested;
+ acquireTokenForManagedIdentityParameters.AttestationTokenProvider =
+ acquireTokenCommonParameters.AttestationTokenProvider;
+
+ // NEW — propagate fallback flag
+ acquireTokenForManagedIdentityParameters.IsBoundTokenFallbackEnabled =
+ acquireTokenCommonParameters.IsBoundTokenFallbackEnabled;
+
+ // existing cache key partitioning...
+}
+```
+
+### 3.3 Capture in `ImdsV2ManagedIdentitySource`
+
+The `ImdsV2ManagedIdentitySource` already captures `_attestationTokenProvider` from `parameters.AttestationTokenProvider` in its `AuthenticateAsync()` method. The new flag must be captured the same way:
+
+```csharp
+internal class ImdsV2ManagedIdentitySource : AbstractManagedIdentity
+{
+ private Func<...> _attestationTokenProvider;
+ private bool _isBoundTokenFallbackEnabled; // NEW
+
+ public override async Task AuthenticateAsync(
+ AcquireTokenForManagedIdentityParameters parameters,
+ CancellationToken cancellationToken)
+ {
+ _attestationTokenProvider = parameters.AttestationTokenProvider;
+ _isBoundTokenFallbackEnabled = parameters.IsBoundTokenFallbackEnabled; // NEW
+
+ // ... existing logic
+ }
+}
+```
+
+This ensures both `_attestationTokenProvider` and `_isBoundTokenFallbackEnabled` are available as instance fields throughout `CreateRequestAsync()`, `GetAttestationJwtAsync()`, and other methods.
+
+---
+
+## 4. Fallback Chain Logic in `ImdsV2ManagedIdentitySource`
+
+### 4.1 Key Type Validation (Replace Throw with Fallback)
+
+**Current code** (`CreateRequestAsync`):
+```csharp
+if (keyInfo.Type != ManagedIdentityKeyType.KeyGuard)
+{
+ throw new MsalClientException(
+ "mtls_pop_requires_keyguard",
+ $"mTLS PoP requires KeyGuard keys. Current key type: {keyInfo.Type}");
+}
+```
+
+**Proposed change:**
+```csharp
+if (keyInfo.Type != ManagedIdentityKeyType.KeyGuard)
+{
+ if (!_isBoundTokenFallbackEnabled)
+ {
+ // Strict mode — throw as before
+ throw new MsalClientException(
+ "mtls_pop_requires_keyguard",
+ $"mTLS PoP requires KeyGuard keys. Current key type: {keyInfo.Type}");
+ }
+
+ // Fallback mode — remove the early gate, let the existing non-attested
+ // path in ExecuteCertificateRequestAsync() handle non-KeyGuard keys
+ _requestContext.Logger.Info(
+ $"[ImdsV2] KeyGuard not available (key type: {keyInfo.Type}). " +
+ "Proceeding with non-attested mTLS PoP binding.");
+}
+```
+
+### 4.2 Attestation Failure (Replace Throw with Fallback)
+
+**Current code** (`GetAttestationJwtAsync`):
+```csharp
+catch (Exception ex)
+{
+ throw new MsalClientException(
+ "attestation_failed",
+ $"[ImdsV2] Attestation token provider failed: {ex.Message}",
+ ex);
+}
+```
+
+**Proposed change:**
+```csharp
+catch (Exception ex)
+{
+ if (!_isBoundTokenFallbackEnabled)
+ {
+ // Strict mode — throw as before
+ throw new MsalClientException(
+ "attestation_failed",
+ $"[ImdsV2] Attestation token provider failed: {ex.Message}",
+ ex);
+ }
+
+ // Fallback mode — swallow attestation failure, proceed without attestation
+ _requestContext.Logger.Warning(
+ $"[ImdsV2] Attestation failed ({ex.Message}). " +
+ "Falling back to non-attested mTLS PoP flow.");
+ return null; // null attestation JWT → non-attested flow
+}
+```
+
+### 4.3 CNG Key Validation (Replace Throw with Fallback)
+
+**Current code** (`GetAttestationJwtAsync`):
+```csharp
+if (keyInfo.Key is not System.Security.Cryptography.RSACng rsaCng)
+{
+ throw new MsalClientException(
+ "credential_guard_requires_cng",
+ "[ImdsV2] Credential Guard attestation currently supports only RSA CNG keys on Windows.");
+}
+```
+
+**Proposed change:**
+```csharp
+if (keyInfo.Key is not System.Security.Cryptography.RSACng rsaCng)
+{
+ if (!_isBoundTokenFallbackEnabled)
+ {
+ throw new MsalClientException(
+ "credential_guard_requires_cng",
+ "[ImdsV2] Credential Guard attestation currently supports only RSA CNG keys.");
+ }
+
+ _requestContext.Logger.Warning(
+ "[ImdsV2] Key is not RSACng, cannot perform Credential Guard attestation. " +
+ "Falling back to non-attested mTLS PoP flow.");
+ return null;
+}
+```
+
+> **Design decision:** Fallback mode absorbs **all attestation-stage failures** — provider exceptions, CNG key type mismatch, and non-KeyGuard keys. The only failures NOT absorbed are platform-level (non-Windows, NET462) and infrastructure-level (IMDSv1 host, IMDS unreachable).
+
+### 4.3 Full Fallback Chain (Summary)
+
+When `WithMtlsPopFallback()` (or `WithMtlsProofOfPossession(new MtlsPopOptions { EnableFallback = true })`) is used, MSAL executes the following chain:
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ WithMtlsPopFallback() + WithAttestationSupport() │
+│ │
+│ 1. Get/create key from key provider │
+│ ├── KeyGuard available? │
+│ │ ├── YES → Key is RSACng? │
+│ │ │ ├── YES → Try attested flow │
+│ │ │ │ ├── Succeeds → Use attested JWT │
+│ │ │ │ └── Fails → Log warning, │
+│ │ │ │ proceed non-attested │
+│ │ │ └── NO → Log warning, proceed non-attested │
+│ │ └── NO → Log info, proceed with available key type │
+│ │ │
+│ 2. Generate CSR with available key │
+│ 3. Request certificate from IMDS V2 │
+│ 4. Acquire token with mTLS binding │
+│ │
+│ Result: Best available bound token │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ WithMtlsPopFallback() WITHOUT WithAttestationSupport() │
+│ │
+│ 1. Get/create key from key provider │
+│ 2. Skip attestation (no provider registered) │
+│ 3. Generate CSR with available key │
+│ 4. Request certificate from IMDS V2 │
+│ 5. Acquire token with non-attested mTLS binding │
+│ │
+│ Result: Non-attested bound token │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 5. Impact on IdWeb (PR #3773)
+
+> **Scope note:** The FIC (Federated Identity Credential) path in IdWeb acquires a managed identity token as a client assertion. This reaches MSAL through `AcquireTokenForManagedIdentity` — the same managed identity builder. No confidential client (`AcquireTokenForClient`) API surface is changed by this proposal.
+
+### Before (Current PR)
+```csharp
+// TokenAcquisition.cs — Pure MSI
+if (isTokenBinding)
+{
+ miBuilder.WithMtlsProofOfPossession()
+ .WithAttestationSupport(); // IdWeb knows about attestation
+}
+
+// ManagedIdentityClientAssertion.cs — FIC
+if (IsTokenBinding)
+{
+ miBuilder.WithMtlsProofOfPossession()
+ .WithAttestationSupport(); // IdWeb knows about attestation
+}
+```
+
+### After — Option 1: `WithMtlsPopFallback()` (Recommended)
+```csharp
+// TokenAcquisition.cs — Pure MSI
+if (isTokenBinding)
+{
+ miBuilder.WithMtlsPopFallback() // "try attested, fall back to non-attested"
+ .WithAttestationSupport(); // Still needed: brings in native DLL
+}
+
+// ManagedIdentityClientAssertion.cs — FIC
+if (IsTokenBinding)
+{
+ miBuilder.WithMtlsPopFallback() // "try attested, fall back to non-attested"
+ .WithAttestationSupport(); // Still needed: brings in native DLL
+}
+```
+
+### After — Option 2: `WithMtlsProofOfPossession(MtlsPopOptions)` (Equivalent, explicit)
+```csharp
+// TokenAcquisition.cs — Pure MSI
+if (isTokenBinding)
+{
+ miBuilder.WithMtlsProofOfPossession(new MtlsPopOptions { EnableFallback = true })
+ .WithAttestationSupport(); // Still needed: brings in native DLL
+}
+
+// ManagedIdentityClientAssertion.cs — FIC
+if (IsTokenBinding)
+{
+ miBuilder.WithMtlsProofOfPossession(new MtlsPopOptions { EnableFallback = true })
+ .WithAttestationSupport(); // Still needed: brings in native DLL
+}
+```
+
+> Both options produce identical behavior. Option 1 is syntactic sugar for Option 2.
+
+### Key Difference
+- **Before:** IdWeb says _"use mTLS PoP **with** attestation"_ (prescriptive — no fallback if attestation fails)
+- **After:** IdWeb says _"get me a bound token with fallback"_ + _"I have attestation capability available"_ (MSAL handles fallback)
+- `WithAttestationSupport()` is now purely a **capability registration** — IdWeb still calls it (and keeps the KeyAttestation package dependency) because it brings in the native DLL, but MSAL decides the fallback policy
+- If attestation fails at runtime, MSAL falls back transparently — IdWeb never sees the failure
+- `WithMtlsProofOfPossession()` (no args) remains available as the strict, no-fallback API
+
+---
+
+## 6. Telemetry & Logging
+
+### New Log Messages
+
+These messages should match the exact strings used in the fallback code paths (Section 4):
+
+| Level | Message | When |
+|-------|---------|------|
+| Info | `[ImdsV2] KeyGuard not available (key type: {type}). Proceeding with non-attested mTLS PoP binding.` | Key provider returns non-KeyGuard key (Section 4.1) |
+| Warning | `[ImdsV2] Attestation failed ({message}). Falling back to non-attested mTLS PoP flow.` | Attestation provider delegate throws (Section 4.2) |
+| Warning | `[ImdsV2] Key is not RSACng, cannot perform Credential Guard attestation. Falling back to non-attested mTLS PoP flow.` | KeyGuard key but not RSACng (Section 4.3) |
+| Info | `[ImdsV2] Attestation token provider not configured. Proceeding with non-attested flow.` | No `WithAttestationSupport()` called (existing behavior, unchanged) |
+
+### Telemetry Events
+
+Add to `ApiEvent` or equivalent:
+
+```csharp
+public enum MtlsBindingOutcome
+{
+ Attested, // Full attested KeyGuard flow
+ NonAttestedKeyGuard, // KeyGuard key, attestation skipped/failed
+ NonAttestedOther, // Hardware or InMemory key
+ NotRequested // No mTLS binding requested
+}
+```
+
+Log `IsBoundTokenFallbackEnabled` in `AcquireTokenForManagedIdentityParameters.LogParameters()` for debugging rollout issues.
+
+---
+
+## 7. Cache Key Partitioning
+
+Two caches are affected by the attestation tag:
+
+### 7.1 Token Cache (in `AcquireTokenForManagedIdentityParameterBuilder.ApplyMtlsPopAndAttestation()`)
+
+Current code partitions by whether `AttestationTokenProvider` is configured:
+```csharp
+acquireTokenCommonParameters.CacheKeyComponents[MiAttCacheKeyComponent] =
+ _ => acquireTokenCommonParameters.AttestationTokenProvider != null ? s_att1 : s_att0;
+```
+
+### 7.2 Cert Cache (in `ImdsV2ManagedIdentitySource.GetMtlsCertCacheKey()`)
+
+Current code also partitions by provider presence:
+```csharp
+return baseKey + (_attestationTokenProvider != null ? AttestationTagEnabled : AttestationTagDisabled);
+```
+
+### 7.3 Problem with Fallback
+
+With fallback enabled, the attestation provider **is** configured (so cache key = `#att=1`), but attestation may **fail** at runtime, producing a non-attested binding. This means:
+- A non-attested cert/token gets cached under the `#att=1` partition
+- If attestation later succeeds (transient failure), the cached non-attested artifact is returned instead of the attested one
+
+### 7.4 Required Change
+
+Both cache keys must be based on the **actual outcome**, not provider presence. However, the cert cache key is computed **before** attestation runs (it's used to look up cached certs). This means:
+
+**Option A (Recommended):** When fallback is enabled, always use `#att=0` for the cache key. Attested and non-attested certs produce functionally equivalent bound tokens — the attestation only affects the trust level of the CSR, not the resulting cert's mTLS binding capability. This avoids the stale-cache problem entirely.
+
+```csharp
+// In GetMtlsCertCacheKey():
+if (_isBoundTokenFallbackEnabled)
+{
+ return baseKey + AttestationTagDisabled; // fallback mode: single partition
+}
+return baseKey + (_attestationTokenProvider != null ? AttestationTagEnabled : AttestationTagDisabled);
+```
+
+**Option B:** Split the cert provisioning into lookup → attestation → cache-store, making the cache key depend on outcome. This is more complex and may not be worth it if Option A is acceptable.
+
+The same approach applies to the token cache key component in `ApplyMtlsPopAndAttestation()`.
+
+---
+
+## 8. API Comparison Matrix
+
+| Aspect | `WithMtlsProofOfPossession()` | `WithMtlsProofOfPossession(MtlsPopOptions)` | `WithMtlsPopFallback()` |
+|--------|-------------------------------|----------------------------------------------|-------------------------|
+| Package | `Microsoft.Identity.Client` | `Microsoft.Identity.Client` | `Microsoft.Identity.Client` |
+| Sets `IsMtlsPopRequested` | ✅ | ✅ | ✅ |
+| Sets `IsBoundTokenFallbackEnabled` | ❌ | Configurable | ✅ (`true`) |
+| Requires KeyGuard | ✅ throws if not | Depends on `EnableFallback` | ❌ falls back |
+| On attestation failure | ❌ throws | Depends on `EnableFallback` | 🔄 falls back to non-attested |
+| Intended caller | Advanced / low-level | Fine-grained control | **IdWeb / higher-level SDKs** |
+| Breaking change | None | None (new overload) | None (new method) |
+
+---
+
+## 9. Breaking Changes
+
+**None.** This is purely additive:
+- `WithMtlsProofOfPossession()` behavior is unchanged (strict, no fallback).
+- `WithAttestationSupport()` behavior is unchanged.
+- `WithMtlsProofOfPossession(MtlsPopOptions)` is a new overload.
+- `WithMtlsPopFallback()` is a new convenience method.
+
+---
+
+## 10. Migration Guide
+
+### For IdWeb — Option 1 (recommended)
+```diff
+- miBuilder.WithMtlsProofOfPossession()
+- .WithAttestationSupport();
++ miBuilder.WithMtlsPopFallback()
++ .WithAttestationSupport();
+```
+
+### For IdWeb — Option 2 (explicit via options)
+```diff
+- miBuilder.WithMtlsProofOfPossession()
+- .WithAttestationSupport();
++ miBuilder.WithMtlsProofOfPossession(new MtlsPopOptions { EnableFallback = true })
++ .WithAttestationSupport();
+```
+
+### For direct MSAL consumers who want strict behavior (no fallback)
+No change needed. Continue using `WithMtlsProofOfPossession()` (no args).
+
+---
+
+## 11. Open Questions
+
+1. **Should `WithMtlsPopFallback()` without `WithAttestationSupport()` log a warning?** Currently it would silently use non-attested flow. Should MSAL log an informational message that attestation capability is not registered?
+
+2. **Should the fallback also cover IMDSv1 hosts?** Currently, if the host only supports IMDSv1 (404 from CSR metadata endpoint), the request throws. Should `WithMtlsPopFallback()` fall back to a bearer token on IMDSv1 hosts? *(Likely no — bound token is the requirement, not optional.)*
+
+3. **Future options:** `MtlsPopOptions` is extensible. Future properties could include things like preferred key type, attestation timeout, or custom fallback policies.