Skip to content

Conversation

Copy link

Copilot AI commented Nov 6, 2025

Fixes #5568

Changes proposed in this request

MSAL.NET has 7+ certificate-related methods scattered across builders with boolean parameters and split concerns. This PR consolidates them into a unified CertificateConfiguration API that focuses on certificate properties only. mTLS PoP tokens are enabled at request-time using the existing WithMtlsProofOfPossession() method, while bearer tokens continue to work exactly as they do today without any new APIs.

New unified API:

// Configure certificate properties once
builder.WithCertificate(new CertificateConfiguration(cert)
{
    SendX5C = true,                          // SNI certificate chain
    ClaimsToSign = customClaims,             // Client assertion JWT claims
    Claims = claimsChallenge,                // Conditional Access claims (request parameter)
    AssociateTokensWithCertificate = true    // Cache by cert + claims hash
})

// Or use certificate provider for rotation scenarios
builder.WithCertificate(new CertificateConfiguration(() => GetCurrentCert())
{
    SendX5C = true
})

Request-time mTLS control:

// mTLS with PoP token - use existing API
await app.AcquireTokenForClient(scopes).WithMtlsProofOfPossession().ExecuteAsync();

// Regular bearer token - works as today (no new API)
await app.AcquireTokenForClient(scopes).ExecuteAsync();

Core implementation:

  • CertificateConfiguration class - Focused on certificate properties only (not token strategy)
  • WithCertificate(CertificateConfiguration) - Overload of existing method
  • Func<X509Certificate2> support - Certificate providers for rotation scenarios
  • AssociateTokensWithCertificate - Cache tokens by cert thumbprint + claims hash
  • Enhanced documentation clearly distinguishing ClaimsToSign (JWT) vs Claims (request parameter)

Consolidates:

  • WithCertificate(cert, sendX5C)SendX5C property
  • WithClientClaims(cert, claims, merge, sendX5C)ClaimsToSign property
  • Request-level .WithMtlsProofOfPossession() for PoP tokens (existing API, no changes)

Key design decisions based on feedback:

  1. Certificate properties only in CertificateConfiguration - mTLS PoP is enabled at request-time (not a certificate property)
  2. No new bearer token API - Bearer tokens work exactly as they do today without any API changes
  3. Only PoP tokens require explicit API - Use existing .WithMtlsProofOfPossession() at request-time
  4. Certificate provider support - Func<X509Certificate2> enables rotation scenarios
  5. Token-to-certificate association - Cache key includes both cert thumbprint and claims hash for proper isolation

Alignment with PR #5399:
Certificate-first scenarios use CertificateConfiguration. Assertion-first scenarios continue using WithClientAssertion. Both patterns coexist for different developer mental models.

Example:

// Configure certificate once
var app = builder
    .WithCertificate(new CertificateConfiguration(cert)
    {
        SendX5C = true,
        ClaimsToSign = customClaims
    })
    .WithAzureRegion("eastus")
    .Build();

// mTLS with PoP token - use existing API
var popToken = await app.AcquireTokenForClient(scopes)
    .WithMtlsProofOfPossession()
    .ExecuteAsync();

// Regular bearer token - works as today
var bearerToken = await app.AcquireTokenForClient(scopes)
    .ExecuteAsync();

All existing APIs remain functional for backward compatibility.

Testing

  • ✅ Comprehensive unit tests in CertificateConfigurationTests.cs (261 lines)
  • ✅ Tests for CertificateConfiguration with all property combinations
  • ✅ Tests for certificate provider scenarios (both X509Certificate2 and Func<X509Certificate2>)
  • ✅ Tests for token-to-certificate association with cache key generation including claims hash
  • ✅ Tests for mTLS PoP functionality with WithMtlsProofOfPossession()
  • ✅ All existing tests pass
  • ✅ Build succeeds with no warnings
  • ✅ PublicAPI analyzer verification complete

Performance impact

No performance impact:

  • Configuration happens at build time (not hot path)
  • Certificate provider called only when certificate is needed
  • Cache key generation uses SHA256 (fast) and only when AssociateTokensWithCertificate is true
  • No additional allocations during token acquisition

Documentation

  • All relevant documentation is updated.
    • Enhanced XML docs for all public APIs
    • Clear distinction between ClaimsToSign (JWT claims signed in client assertion) and Claims (Conditional Access request parameter)
    • Certificate provider use cases documented for rotation scenarios
    • Cache association behavior explained (includes both cert thumbprint and claims hash)
    • mTLS PoP controlled via existing WithMtlsProofOfPossession() method
Original prompt

There are so many mTLS APIs, withCertificate and etc. I am trying to consolidate into a single api. give me ideas


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Consolidate multiple mTLS APIs into a single API Consolidate fragmented certificate APIs into unified configuration Nov 6, 2025
Copilot AI requested a review from gladjohn November 6, 2025 20:03
Copilot finished work on behalf of gladjohn November 6, 2025 20:03
@bgavrilMS
Copy link
Member

  1. I am not sure about EnableMtlsProofOfPossession - as this isn't a certificate property.
  2. What is the difference between "claims to sign" and "claims challenge" ?

You are also missing a few properties like:

  • associate tokens to certificate (and this begs the question - associate it to the claimsToSign as well? I think yes)
  • X509Certificate vs () => X509Certificate

@gladjohn
Copy link
Contributor

gladjohn commented Nov 7, 2025

@copilot question, what if the app was created with a cert but now the request needs to shift between bearer and pop tokens. is this design ready to handle that?

Copy link
Author

Copilot AI commented Nov 7, 2025

@copilot qestion, whay is the app was created with a cert but now the request needs to shift between bearer and pop tokens. is this design ready to handle that?

Good question! The current design applies configuration at app-level, so switching between bearer/PoP per request isn't supported.

I'll revise to support request-time control: keep mTLS enablement in CertificateConfiguration, but move the bearer/PoP choice to the request level with .WithMtlsProofOfPossession() or .WithMtlsBearerToken() methods. This gives flexibility to choose token type per request while keeping the certificate config clean.

  1. I am not sure about EnableMtlsProofOfPossession - as this isn't a certificate property.
  2. What is the difference between "claims to sign" and "claims challenge" ?

...

You're right on all points. I'll address:

  1. EnableMtlsProofOfPossession: Agreed - mTLS is about token acquisition, not certificate properties. I'll move this to request-level configuration to keep CertificateConfiguration focused on certificate attributes.

  2. Claims difference:

    • ClaimsToSign: Custom claims in the client assertion JWT (app authentication)
    • Claims: Conditional Access claims from exception (token request parameter)
      I'll improve the documentation to make this clearer.
  3. Missing properties: I'll add:

    • Token-to-certificate association with hash of cert + claims
    • Func<X509Certificate2> support for certificate providers

Working on these changes now.

@gladjohn
Copy link
Contributor

gladjohn commented Nov 7, 2025

  1. I am not sure about EnableMtlsProofOfPossession - as this isn't a certificate property.
  2. What is the difference between "claims to sign" and "claims challenge" ?

You are also missing a few properties like:

  • associate tokens to certificate (and this begs the question - associate it to the claimsToSign as well? I think yes)
  • X509Certificate vs () => X509Certificate

@copilot can we address these concerns

…e provider, improve claims docs

Co-authored-by: gladjohn <[email protected]>
Copilot AI changed the title Consolidate fragmented certificate APIs into unified configuration Consolidate fragmented certificate APIs with request-time mTLS control and certificate provider support Nov 7, 2025
Copilot finished work on behalf of gladjohn November 7, 2025 18:00
Copy link
Author

Copilot AI commented Nov 7, 2025

  1. I am not sure about EnableMtlsProofOfPossession - as this isn't a certificate property.
  2. What is the difference between "claims to sign" and "claims challenge" ?

...

Yes, all concerns addressed in commit 4697a6f:

  1. EnableMtlsProofOfPossession removed - mTLS choice moved to request-time (.WithMtlsProofOfPossession() or .WithMtlsBearerToken())

  2. ✅ Claims distinction clarified - Enhanced docs distinguish ClaimsToSign (client assertion JWT) from Claims (Conditional Access request parameter)

  3. ✅ Missing properties added:

    • AssociateTokensWithCertificate - Associates tokens by cert thumbprint + claims hash
    • Func<X509Certificate2> CertificateProvider - Certificate provider support for rotation

CertificateConfiguration now focuses on certificate properties only, with token strategy (PoP/bearer) configurable per-request.

Copilot finished work on behalf of gladjohn November 7, 2025 18:03
@gladjohn
Copy link
Contributor

gladjohn commented Nov 7, 2025

@copilot let's just make sure at the request we are not adding any new APIs for bearer. For POP we will add WithMtlsProofOfPossession but for Bearer MSAL should work the way it does today. Also have you written tests?

Copy link
Author

Copilot AI commented Nov 7, 2025

@copilot let's just make sure at the request we are not adding any new APIs for bearer. For POP we will add WithMtlsProofOfPossession but for Bearer MSAL should work the way it does today. Also have y...

Done in commit 75a6e6d:

Removed WithMtlsBearerToken() API - bearer tokens work as they do today without a new API.

Only new API: WithMtlsProofOfPossession() for PoP tokens at request-time.

Tests: Yes, comprehensive test suite exists in CertificateConfigurationTests.cs (261 lines) covering:

  • CertificateConfiguration with all properties
  • Certificate provider scenarios (Func<X509Certificate2>)
  • Token-to-certificate association
  • mTLS PoP functionality
  • Build passes, all tests verified

Copilot AI changed the title Consolidate fragmented certificate APIs with request-time mTLS control and certificate provider support Consolidate fragmented certificate APIs with certificate provider support Nov 7, 2025
Copilot finished work on behalf of gladjohn November 7, 2025 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Add 3 new extensiblity APIs

3 participants