Credential system refactor: unified context, canonical matrix, eliminate dead code#5748
Credential system refactor: unified context, canonical matrix, eliminate dead code#5748Copilot wants to merge 14 commits into
Conversation
Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
…eters Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
|
@copilot build error "src\client\Microsoft.Identity.Client\MsalError.cs(1215,29): Error RS0016: Symbol 'InvalidCredentialMaterial' is not part of the declared public API (https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md)" |
…er error Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Fixed in d09d962. Added |
…CredentialMaterialAsync via orchestrator Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
…credential system Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
…icException Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
| public long ResolutionTimeMs { get; } | ||
| } | ||
|
|
||
| internal enum CredentialType |
There was a problem hiding this comment.
Classes already implement an interface, so there is no need to hold duplicate information. We should at most use this for quick telemetry resolution.
There was a problem hiding this comment.
In #5835 this was taken further: CredentialSource is no longer part of CredentialMaterial; the only remaining cleanup there is deleting the unused enum file.
| /// <summary> | ||
| /// Whether mTLS binding was requested (MtlsRequired in context). | ||
| /// </summary> | ||
| public bool MtlsCertificateRequested { get; } |
There was a problem hiding this comment.
It is not the credential's concern. It should not be part of the state of the credential.
There was a problem hiding this comment.
Carried forward in #5835 by consolidating the credential-resolution inputs into CredentialContext and removing mode state from the credential itself.
| /// Hash prefix of mTLS certificate (first 8-16 chars of SHA-256 hash of RawData). | ||
| /// Not full thumbprint; used for correlation only. | ||
| /// </summary> | ||
| public string MtlsCertificateIdHashPrefix { get; } |
There was a problem hiding this comment.
Do we really need to cache this?
There was a problem hiding this comment.
In #5835 the token endpoint is resolved once in TokenClient and reused; it is not cached within the credential or context.
| public string CredentialSource { get; } | ||
|
|
||
| /// <summary> | ||
| /// Hash prefix of mTLS certificate (first 8-16 chars of SHA-256 hash of RawData). |
There was a problem hiding this comment.
How can the hash prefix be useful ? it is not a good identifier. You cannot guarantee that the first 8-16 chars of a hash provide reasonable uniquness.
There was a problem hiding this comment.
Carried forward in #5835; the hash-prefix field is no longer part of the material model.
| /// <summary> | ||
| /// Cancellation token for async operations. | ||
| /// </summary> | ||
| public CancellationToken CancellationToken { get; init; } |
There was a problem hiding this comment.
Keep this separate pls. CancellationToken is always the last param of an async method.
There was a problem hiding this comment.
Carried forward in #5835; CancellationToken is no longer part of CredentialContext and is passed explicitly to the async methods.
| /// <summary> | ||
| /// FMI path for client assertion (Federated Managed Identity). | ||
| /// </summary> | ||
| public string ClientAssertionFmiPath { get; init; } |
| // Validate structure | ||
| if (material == null) | ||
| { | ||
| throw new MsalClientException( |
There was a problem hiding this comment.
This is not a good exception. CX can't do anything about this. This is a bug in MSAL, so InvalidOperationException is more appropiate.
There was a problem hiding this comment.
Carried forward in #5835; internal invariants now use InvalidOperationException, while invalid credential/mode combinations still throw MsalClientException.
| "Credential returned null material."); | ||
| } | ||
|
|
||
| if (material.TokenRequestParameters == null) |
There was a problem hiding this comment.
Carried forward in #5835; the exception taxonomy has been refined so that internal invariant failures use InvalidOperationException.
| /// - Validating material structure | ||
| /// - Enforcing mTLS constraints | ||
| /// </summary> | ||
| internal static class CredentialMaterialOrchestrator |
There was a problem hiding this comment.
But I don't see the point of this class. ICredential should have ResolveAsync
There was a problem hiding this comment.
Carried forward in #5835; the active type is CredentialMaterialResolver.
| /// Contains only what credentials need to produce material. | ||
| /// Immutable by design. | ||
| /// </summary> | ||
| internal readonly record struct CredentialRequestContext |
There was a problem hiding this comment.
Don't we already have a public class for this ? AssertionRequestOptions ? Can we add more stuff to that one instead - with internal? Objects (even data objects) are expensive.
There was a problem hiding this comment.
carried forward in #5835 with a small internal CredentialContext
| /// When true, certificate should be used for TLS client auth only (mTLS bearer mode). | ||
| /// No JWT client_assertion should be sent. | ||
| /// </summary> | ||
| public bool MtlsBearerMode { get; init; } |
There was a problem hiding this comment.
It looks like you went with 2 flags. This is confusing. I would not have any flags in this object. Instead, the IClientCreential can have a method like Task<CredentialResult> ResolveAsync(bool forAcquisitionOverMtls, CredentialContext context, CancellationToken token)
There was a problem hiding this comment.
Carried forward in #5835 as OAuthMode; the credential resolution now takes the mode as an explicit parameter rather than storing flags in the context object.
- Replace MtlsRequired/MtlsBearerMode with ClientAuthMode enum - Rename CredentialRequestContext to CredentialContext - Merge MtlsValidationContext into CredentialContext - Rename MtlsCertificate to ResolvedCertificate - Remove CancellationToken from context, add as explicit parameter - Delete CredentialMaterialMetadata entirely - Remove CredentialMaterialHelper entirely - Add CredentialSource enum - Rename CredentialMaterialOrchestrator to CredentialMaterialResolver - Update exception taxonomy (InvalidOperationException for invariants) Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
|
@neha-bhargava @bgavrilMS All comments from #5748 are now accounted for with corresponding addressed items in #5835. |
Changes proposed in this request
Comprehensive refactor addressing 32 code review comments. Simplifies architecture, enforces canonical credential×mode matrix, eliminates unused telemetry infrastructure.
Architecture
ClientAuthModeenum replacesMtlsRequired/MtlsBearerModebooleansMtlsValidationContextintoCredentialContext(renamed fromCredentialRequestContext)CancellationTokennow explicit parameter, not in context structMtlsCertificate→ResolvedCertificate(mode-agnostic naming)CredentialMaterialOrchestrator→CredentialMaterialResolverDead code removed (~370 LOC)
CredentialMaterialMetadata,CredentialMaterialHelper,MtlsValidationContextclassesCanonical matrix enforcement
Matrix documented in
CREDENTIAL_MATRIX.cs. Credentials validate mode compatibility, throwMsalClientExceptionfor unsupported combinations. Resolver throwsInvalidOperationExceptionfor invariant violations (null material/params).Correctness fixes
TokenBindingCertificatepresent (confirmation claim presence determines type). Fixes SNI assertion flow AADSTS3921994CryptographicException→MsalClientExceptionfor disposed certificatesCredentialSourcestring → enumOutput guarantees
TokenRequestParametersnever null (empty dict for TLS-only scenarios)ResolvedCertificateset whenever cert obtained (any mode)Testing
Added
CredentialMatrixTests.cswith 16 tests:Existing integration tests pass unchanged.
Performance impact
Removed ~100ns Stopwatch overhead per credential resolution.
Documentation
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.