From d15b71d2de55da97fd10b000625da8ddf24b30f7 Mon Sep 17 00:00:00 2001 From: trwalke Date: Mon, 3 Nov 2025 03:55:10 -0800 Subject: [PATCH] Initial prototype --- .../Bearer/BearerAuthenticationOperation.cs | 6 ++++++ .../AuthScheme/IAuthenticationOperation.cs | 7 +++++++ .../AuthScheme/MsalCacheValidationData.cs | 18 ++++++++++++++++++ .../PoP/MtlsPopAuthenticationOperation.cs | 6 ++++++ .../PoP/PopAuthenticationOperation.cs | 6 ++++++ .../PoP/PopBrokerAuthenticationOperation.cs | 6 ++++++ .../SSHCertAuthenticationOperation.cs | 6 ++++++ .../Extensibility/ExternalBoundTokenScheme.cs | 6 ++++++ .../Requests/ClientCredentialRequest.cs | 14 ++++++++++++++ .../Microsoft.Identity.Client.csproj | 5 +++++ .../PublicApi/net462/PublicAPI.Unshipped.txt | 4 ++++ .../PublicApi/net472/PublicAPI.Unshipped.txt | 4 ++++ .../net8.0-android/PublicAPI.Unshipped.txt | 4 ++++ .../net8.0-ios/PublicAPI.Unshipped.txt | 4 ++++ .../PublicApi/net8.0/PublicAPI.Unshipped.txt | 4 ++++ .../netstandard2.0/PublicAPI.Unshipped.txt | 4 ++++ 16 files changed, 104 insertions(+) create mode 100644 src/client/Microsoft.Identity.Client/AuthScheme/MsalCacheValidationData.cs diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/Bearer/BearerAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/Bearer/BearerAuthenticationOperation.cs index 4e1dc687e9..5e5899d703 100644 --- a/src/client/Microsoft.Identity.Client/AuthScheme/Bearer/BearerAuthenticationOperation.cs +++ b/src/client/Microsoft.Identity.Client/AuthScheme/Bearer/BearerAuthenticationOperation.cs @@ -30,5 +30,11 @@ public IReadOnlyDictionary GetTokenRequestParams() // ESTS issues Bearer tokens by default, no need for any extra params return CollectionHelpers.GetEmptyDictionary(); } + + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem) + { + // no-op + return true; + } } } diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/IAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/IAuthenticationOperation.cs index 1ed04ea6e2..1718872d1f 100644 --- a/src/client/Microsoft.Identity.Client/AuthScheme/IAuthenticationOperation.cs +++ b/src/client/Microsoft.Identity.Client/AuthScheme/IAuthenticationOperation.cs @@ -43,6 +43,13 @@ public interface IAuthenticationOperation /// Name and values of params IReadOnlyDictionary GetTokenRequestParams(); + /// + /// + /// + /// + /// + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem); + /// /// Key ID of the public / private key pair used by the encryption algorithm, if any. /// Tokens obtained by authentication schemes that use this are bound to the KeyId, i.e. diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/MsalCacheValidationData.cs b/src/client/Microsoft.Identity.Client/AuthScheme/MsalCacheValidationData.cs new file mode 100644 index 0000000000..a0e95d28c8 --- /dev/null +++ b/src/client/Microsoft.Identity.Client/AuthScheme/MsalCacheValidationData.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; + +namespace Microsoft.Identity.Client.AuthScheme +{ + /// + /// Data used to validate cache items for different authentication schemes. + /// + public class MsalCacheValidationData + { + /// + /// Gets the persisted parameters addded to the cache items. + /// + public IDictionary PersistedCacheParameters { get; internal set; } + } +} diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs index 980ac388f7..ee79d30564 100644 --- a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs +++ b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/MtlsPopAuthenticationOperation.cs @@ -40,5 +40,11 @@ public void FormatResult(AuthenticationResult authenticationResult) { authenticationResult.BindingCertificate = _mtlsCert; } + + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem) + { + // no-op + return true; + } } } diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopAuthenticationOperation.cs index a2723fe9b9..b52950a643 100644 --- a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopAuthenticationOperation.cs +++ b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopAuthenticationOperation.cs @@ -162,5 +162,11 @@ private string CreateJWS(string payload, string header) return sb.ToString(); } + + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem) + { + // no-op + return true; + } } } diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopBrokerAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopBrokerAuthenticationOperation.cs index c81f411f41..55dfb397be 100644 --- a/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopBrokerAuthenticationOperation.cs +++ b/src/client/Microsoft.Identity.Client/AuthScheme/PoP/PopBrokerAuthenticationOperation.cs @@ -34,5 +34,11 @@ public IReadOnlyDictionary GetTokenRequestParams() { return CollectionHelpers.GetEmptyDictionary(); } + + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem) + { + // no-op + return true; + } } } diff --git a/src/client/Microsoft.Identity.Client/AuthScheme/SSHCertificates/SSHCertAuthenticationOperation.cs b/src/client/Microsoft.Identity.Client/AuthScheme/SSHCertificates/SSHCertAuthenticationOperation.cs index cc64c2db61..198133298f 100644 --- a/src/client/Microsoft.Identity.Client/AuthScheme/SSHCertificates/SSHCertAuthenticationOperation.cs +++ b/src/client/Microsoft.Identity.Client/AuthScheme/SSHCertificates/SSHCertAuthenticationOperation.cs @@ -53,5 +53,11 @@ public IReadOnlyDictionary GetTokenRequestParams() { OAuth2Parameter.RequestConfirmation , _jwk } }; } + + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem) + { + // no-op + return true; + } } } diff --git a/src/client/Microsoft.Identity.Client/Extensibility/ExternalBoundTokenScheme.cs b/src/client/Microsoft.Identity.Client/Extensibility/ExternalBoundTokenScheme.cs index d622bbbae7..9c4fe976ff 100644 --- a/src/client/Microsoft.Identity.Client/Extensibility/ExternalBoundTokenScheme.cs +++ b/src/client/Microsoft.Identity.Client/Extensibility/ExternalBoundTokenScheme.cs @@ -37,5 +37,11 @@ public IReadOnlyDictionary GetTokenRequestParams() { return CollectionHelpers.GetEmptyDictionary(); } + + bool ValidateCachedToken(MsalCacheValidationData cachedTokenItem) + { + // no-op + return true; + } } } diff --git a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs index e7c08f0fc8..d15aa6bb51 100644 --- a/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs +++ b/src/client/Microsoft.Identity.Client/Internal/Requests/ClientCredentialRequest.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Identity.Client.ApiConfig.Parameters; +using Microsoft.Identity.Client.AuthScheme; using Microsoft.Identity.Client.Cache.Items; using Microsoft.Identity.Client.Core; using Microsoft.Identity.Client.Extensibility; @@ -76,6 +77,19 @@ protected override async Task ExecuteAsync(CancellationTok MsalAccessTokenCacheItem cachedAccessTokenItem = await GetCachedAccessTokenAsync().ConfigureAwait(false); + // Validate the cached token using the authentication operation + if (AuthenticationRequestParameters.AuthenticationScheme != null && + cachedAccessTokenItem != null) + { + var cacheValidationData = new MsalCacheValidationData(); + cacheValidationData.PersistedCacheParameters = cachedAccessTokenItem.PersistedCacheParameters; + if (!AuthenticationRequestParameters.AuthenticationScheme.ValidateCachedToken(cacheValidationData)) + { + logger.Info("[ClientCredentialRequest] Cached token failed authentication operation validation."); + cachedAccessTokenItem = null; + } + } + // No access token or cached access token needs to be refreshed if (cachedAccessTokenItem != null) { diff --git a/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj b/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj index 3279f0338a..d80d8cf17a 100644 --- a/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj +++ b/src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj @@ -80,6 +80,7 @@ + @@ -161,4 +162,8 @@ + + + + diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt index 4971f00461..134e4ba292 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net462/PublicAPI.Unshipped.txt @@ -2,6 +2,10 @@ const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPrev const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string +Microsoft.Identity.Client.AuthScheme.ValidateCachedToken(Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData cachedTokenItem) -> bool +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.MsalCacheValidationData() -> void +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.PersistedCacheParameters.get -> System.Collections.Generic.IDictionary Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt index 4971f00461..134e4ba292 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net472/PublicAPI.Unshipped.txt @@ -2,6 +2,10 @@ const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPrev const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string +Microsoft.Identity.Client.AuthScheme.ValidateCachedToken(Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData cachedTokenItem) -> bool +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.MsalCacheValidationData() -> void +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.PersistedCacheParameters.get -> System.Collections.Generic.IDictionary Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt index 4971f00461..134e4ba292 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-android/PublicAPI.Unshipped.txt @@ -2,6 +2,10 @@ const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPrev const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string +Microsoft.Identity.Client.AuthScheme.ValidateCachedToken(Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData cachedTokenItem) -> bool +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.MsalCacheValidationData() -> void +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.PersistedCacheParameters.get -> System.Collections.Generic.IDictionary Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt index 4971f00461..134e4ba292 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0-ios/PublicAPI.Unshipped.txt @@ -2,6 +2,10 @@ const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPrev const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string +Microsoft.Identity.Client.AuthScheme.ValidateCachedToken(Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData cachedTokenItem) -> bool +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.MsalCacheValidationData() -> void +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.PersistedCacheParameters.get -> System.Collections.Generic.IDictionary Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder diff --git a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt index 4971f00461..134e4ba292 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/net8.0/PublicAPI.Unshipped.txt @@ -2,6 +2,10 @@ const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPrev const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string +Microsoft.Identity.Client.AuthScheme.ValidateCachedToken(Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData cachedTokenItem) -> bool +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.MsalCacheValidationData() -> void +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.PersistedCacheParameters.get -> System.Collections.Generic.IDictionary Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder diff --git a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt index 4971f00461..134e4ba292 100644 --- a/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/client/Microsoft.Identity.Client/PublicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -2,6 +2,10 @@ const Microsoft.Identity.Client.MsalError.CannotSwitchBetweenImdsVersionsForPrev const Microsoft.Identity.Client.MsalError.InvalidCertificate = "invalid_certificate" -> string const Microsoft.Identity.Client.MsalError.MtlsNotSupportedForManagedIdentity = "mtls_not_supported_for_managed_identity" -> string const Microsoft.Identity.Client.MsalError.MtlsPopTokenNotSupportedinImdsV1 = "mtls_pop_token_not_supported_in_imds_v1" -> string +Microsoft.Identity.Client.AuthScheme.ValidateCachedToken(Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData cachedTokenItem) -> bool +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.MsalCacheValidationData() -> void +Microsoft.Identity.Client.AuthScheme.MsalCacheValidationData.PersistedCacheParameters.get -> System.Collections.Generic.IDictionary Microsoft.Identity.Client.ManagedIdentityApplication.GetManagedIdentitySourceAsync() -> System.Threading.Tasks.Task Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource.ImdsV2 = 8 -> Microsoft.Identity.Client.ManagedIdentity.ManagedIdentitySource Microsoft.Identity.Client.ManagedIdentityApplicationBuilder.WithExtraQueryParameters(System.Collections.Generic.IDictionary extraQueryParameters) -> Microsoft.Identity.Client.ManagedIdentityApplicationBuilder