Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3d96726
Iniial work
tlupes Mar 11, 2026
eacf4f3
UTs for mTLS
tlupes Mar 11, 2026
735f6c8
Merge branch 'master' of https://github.com/AzureAD/microsoft-identit…
tlupes Mar 11, 2026
f7b4a3e
Unit test fixes
tlupes Mar 11, 2026
9f523e5
Improvements
tlupes Mar 11, 2026
824f691
Wire in the new properties
tlupes Mar 17, 2026
e0415e9
Merge branch 'master' into mTLS
tlupes Mar 17, 2026
2a90dd2
Fixes
tlupes Mar 17, 2026
d719049
Merge branch 'mTLS' of https://github.com/tlupes/microsoft-identity-w…
tlupes Mar 17, 2026
6430f2d
Add sample
tlupes Mar 19, 2026
8028e78
Add Comments
tlupes Mar 23, 2026
667df65
Merge branch 'master' into mTLS
tlupes Mar 23, 2026
5643ca7
Set constructors to not obsolete
tlupes Mar 23, 2026
d52cc88
Merge branch 'mTLS' of https://github.com/tlupes/microsoft-identity-w…
tlupes Mar 23, 2026
2b4d359
Merge branch 'master' into mTLS
tlupes Mar 26, 2026
f1542bf
Remove obsolete methods
tlupes Mar 30, 2026
a57b7df
Merge branch 'mTLS' of https://github.com/tlupes/microsoft-identity-w…
tlupes Mar 30, 2026
8640aea
Merge branch 'master' into mTLS
tlupes Mar 31, 2026
25fa064
Merge branch 'master' into mTLS
tlupes Apr 16, 2026
5b05181
Fixes for service registration
tlupes Apr 16, 2026
ada00ce
Merge branch 'master' into mTLS
tlupes Apr 16, 2026
ff58db0
Fix merge issues
tlupes Apr 16, 2026
d193b5e
Fix PublicAPIs
tlupes Apr 17, 2026
3036f29
Fix TokenAcquisitionAuthorityTests
tlupes Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Microsoft.Identity.Web.sln
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireBlazorCallsWebApi.Web
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireBlazorCallsWebApi.AppHost", "tests\DevApps\AspireBlazorCallsWebApi\AspireBlazorCallsWebApi.AppHost\AspireBlazorCallsWebApi.AppHost.csproj", "{CF9A0AD1-477A-AC6E-E94E-3E6D5D8F07BE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mtls", "Mtls", "{D70AD3EC-52EE-495D-BA5E-C9671601C699}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MtlsClient", "tests\DevApps\Mtls\MtlsClient\MtlsClient.csproj", "{CF703B6E-6F28-8E41-484C-FFDA485A1293}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MtlsWebApi", "tests\DevApps\Mtls\MtlsWebApi\MtlsWebApi.csproj", "{270F5686-0E0B-74E6-E66F-87C54BEF24DE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -482,6 +488,14 @@ Global
{CF9A0AD1-477A-AC6E-E94E-3E6D5D8F07BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF9A0AD1-477A-AC6E-E94E-3E6D5D8F07BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF9A0AD1-477A-AC6E-E94E-3E6D5D8F07BE}.Release|Any CPU.Build.0 = Release|Any CPU
{CF703B6E-6F28-8E41-484C-FFDA485A1293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF703B6E-6F28-8E41-484C-FFDA485A1293}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF703B6E-6F28-8E41-484C-FFDA485A1293}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF703B6E-6F28-8E41-484C-FFDA485A1293}.Release|Any CPU.Build.0 = Release|Any CPU
{270F5686-0E0B-74E6-E66F-87C54BEF24DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{270F5686-0E0B-74E6-E66F-87C54BEF24DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{270F5686-0E0B-74E6-E66F-87C54BEF24DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{270F5686-0E0B-74E6-E66F-87C54BEF24DE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -569,6 +583,9 @@ Global
{106919C1-549A-AEAE-9925-E9E50B81BD23} = {688187B8-8AEF-4C80-948B-92BCCDE86D76}
{5373EB99-4B37-B3B3-8280-2A1EDB79F198} = {688187B8-8AEF-4C80-948B-92BCCDE86D76}
{CF9A0AD1-477A-AC6E-E94E-3E6D5D8F07BE} = {688187B8-8AEF-4C80-948B-92BCCDE86D76}
{D70AD3EC-52EE-495D-BA5E-C9671601C699} = {7786D2DD-9EE4-42E1-B587-740A2E15C41D}
{CF703B6E-6F28-8E41-484C-FFDA485A1293} = {D70AD3EC-52EE-495D-BA5E-C9671601C699}
{270F5686-0E0B-74E6-E66F-87C54BEF24DE} = {D70AD3EC-52EE-495D-BA5E-C9671601C699}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {104367F1-CE75-4F40-B32F-F14853973187}
Expand Down
225 changes: 195 additions & 30 deletions src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Identity.Web.DownstreamApi.DownstreamApi(Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider! authorizationHeaderProvider, Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Identity.Abstractions.DownstreamApiOptions!>! namedDownstreamApiOptions, System.Net.Http.IHttpClientFactory! httpClientFactory, Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DownstreamApi!>! logger, Microsoft.Identity.Web.ICredentialsProvider! credentialsProvider, Microsoft.Identity.Client.IMsalHttpClientFactory? msalHttpClientFactory) -> void
Microsoft.Identity.Web.DownstreamApi.UpdateRequestWithCertificateAsync(System.Net.Http.HttpRequestMessage! httpRequestMessage, System.Net.Http.HttpContent? content, Microsoft.Identity.Abstractions.DownstreamApiOptions! effectiveOptions, bool appToken, System.Security.Claims.ClaimsPrincipal? user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.Identity.Abstractions.AuthorizationHeaderInformation? HeaderInfo, Microsoft.Identity.Abstractions.CredentialDescription? MtlsCredential)>!
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Identity.Web.DownstreamApi.DownstreamApi(Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider! authorizationHeaderProvider, Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Identity.Abstractions.DownstreamApiOptions!>! namedDownstreamApiOptions, System.Net.Http.IHttpClientFactory! httpClientFactory, Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DownstreamApi!>! logger, Microsoft.Identity.Web.ICredentialsProvider! credentialsProvider, Microsoft.Identity.Client.IMsalHttpClientFactory? msalHttpClientFactory) -> void
Microsoft.Identity.Web.DownstreamApi.UpdateRequestWithCertificateAsync(System.Net.Http.HttpRequestMessage! httpRequestMessage, System.Net.Http.HttpContent? content, Microsoft.Identity.Abstractions.DownstreamApiOptions! effectiveOptions, bool appToken, System.Security.Claims.ClaimsPrincipal? user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.Identity.Abstractions.AuthorizationHeaderInformation? HeaderInfo, Microsoft.Identity.Abstractions.CredentialDescription? MtlsCredential)>!
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Identity.Web.DownstreamApi.DownstreamApi(Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider! authorizationHeaderProvider, Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Identity.Abstractions.DownstreamApiOptions!>! namedDownstreamApiOptions, System.Net.Http.IHttpClientFactory! httpClientFactory, Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DownstreamApi!>! logger, Microsoft.Identity.Web.ICredentialsProvider! credentialsProvider, Microsoft.Identity.Client.IMsalHttpClientFactory? msalHttpClientFactory) -> void
Microsoft.Identity.Web.DownstreamApi.UpdateRequestWithCertificateAsync(System.Net.Http.HttpRequestMessage! httpRequestMessage, System.Net.Http.HttpContent? content, Microsoft.Identity.Abstractions.DownstreamApiOptions! effectiveOptions, bool appToken, System.Security.Claims.ClaimsPrincipal? user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.Identity.Abstractions.AuthorizationHeaderInformation? HeaderInfo, Microsoft.Identity.Abstractions.CredentialDescription? MtlsCredential)>!
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Identity.Web.DownstreamApi.DownstreamApi(Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider! authorizationHeaderProvider, Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Identity.Abstractions.DownstreamApiOptions!>! namedDownstreamApiOptions, System.Net.Http.IHttpClientFactory! httpClientFactory, Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DownstreamApi!>! logger, Microsoft.Identity.Web.ICredentialsProvider! credentialsProvider, Microsoft.Identity.Client.IMsalHttpClientFactory? msalHttpClientFactory) -> void
Microsoft.Identity.Web.DownstreamApi.UpdateRequestWithCertificateAsync(System.Net.Http.HttpRequestMessage! httpRequestMessage, System.Net.Http.HttpContent? content, Microsoft.Identity.Abstractions.DownstreamApiOptions! effectiveOptions, bool appToken, System.Security.Claims.ClaimsPrincipal? user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.Identity.Abstractions.AuthorizationHeaderInformation? HeaderInfo, Microsoft.Identity.Abstractions.CredentialDescription? MtlsCredential)>!
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Identity.Web.DownstreamApi.DownstreamApi(Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider! authorizationHeaderProvider, Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Identity.Abstractions.DownstreamApiOptions!>! namedDownstreamApiOptions, System.Net.Http.IHttpClientFactory! httpClientFactory, Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DownstreamApi!>! logger, Microsoft.Identity.Web.ICredentialsProvider! credentialsProvider, Microsoft.Identity.Client.IMsalHttpClientFactory? msalHttpClientFactory) -> void
Microsoft.Identity.Web.DownstreamApi.UpdateRequestWithCertificateAsync(System.Net.Http.HttpRequestMessage! httpRequestMessage, System.Net.Http.HttpContent? content, Microsoft.Identity.Abstractions.DownstreamApiOptions! effectiveOptions, bool appToken, System.Security.Claims.ClaimsPrincipal? user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.Identity.Abstractions.AuthorizationHeaderInformation? HeaderInfo, Microsoft.Identity.Abstractions.CredentialDescription? MtlsCredential)>!
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
Microsoft.Identity.Web.DownstreamApi.DownstreamApi(Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider! authorizationHeaderProvider, Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Identity.Abstractions.DownstreamApiOptions!>! namedDownstreamApiOptions, System.Net.Http.IHttpClientFactory! httpClientFactory, Microsoft.Extensions.Logging.ILogger<Microsoft.Identity.Web.DownstreamApi!>! logger, Microsoft.Identity.Web.ICredentialsProvider! credentialsProvider, Microsoft.Identity.Client.IMsalHttpClientFactory? msalHttpClientFactory) -> void
Microsoft.Identity.Web.DownstreamApi.UpdateRequestWithCertificateAsync(System.Net.Http.HttpRequestMessage! httpRequestMessage, System.Net.Http.HttpContent? content, Microsoft.Identity.Abstractions.DownstreamApiOptions! effectiveOptions, bool appToken, System.Security.Claims.ClaimsPrincipal? user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.Identity.Abstractions.AuthorizationHeaderInformation? HeaderInfo, Microsoft.Identity.Abstractions.CredentialDescription? MtlsCredential)>!
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,13 @@ internal class TokenAcquisitionAspNetCore : TokenAcquisition, ITokenAcquisitionI
/// <param name="httpClientFactory">HTTP client factory.</param>
/// <param name="logger">Logger.</param>
/// <param name="serviceProvider">Service provider.</param>
/// <param name="credentialsLoader">Credential loader service.</param>
public TokenAcquisitionAspNetCore(
IMsalTokenCacheProvider tokenCacheProvider,
IHttpClientFactory httpClientFactory,
ILogger<TokenAcquisition> logger,
ITokenAcquisitionHost tokenAcquisitionHost,
IServiceProvider serviceProvider,
ICredentialsLoader credentialsLoader) :
base(tokenCacheProvider, tokenAcquisitionHost, httpClientFactory, logger, serviceProvider, credentialsLoader)
IServiceProvider serviceProvider) :
base(tokenCacheProvider, tokenAcquisitionHost, httpClientFactory, logger, serviceProvider)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Client;
using static Microsoft.Identity.Web.TokenAcquisition;

namespace Microsoft.Identity.Web
{
internal static partial class ConfidentialClientApplicationBuilderExtension
{
[Obsolete(IDWebErrorMessage.WithClientCredentialsIsObsolete, false)]
public static ConfidentialClientApplicationBuilder WithClientCredentials(
this ConfidentialClientApplicationBuilder builder,
IEnumerable<CredentialDescription> clientCredentials,
ILogger logger,
ICredentialsLoader credentialsLoader,
CredentialSourceLoaderParameters credentialSourceLoaderParameters)
{
return WithClientCredentialsAsync(
builder,
clientCredentials,
logger,
credentialsLoader,
credentialSourceLoaderParameters,
isTokenBinding: false).GetAwaiter().GetResult();
}

public static async Task<ConfidentialClientApplicationBuilder> WithClientCredentialsAsync(
this ConfidentialClientApplicationBuilder builder,
IEnumerable<CredentialDescription> clientCredentials,
ILogger logger,
ICredentialsLoader credentialsLoader,
MergedOptions mergedOptions,
ICredentialsProvider credentialsProvider,
CredentialSourceLoaderParameters? credentialSourceLoaderParameters,
bool isTokenBinding)
bool isTokenBinding,
CancellationToken cancellationToken = default)
{
var credential = await LoadCredentialForMsalOrFailAsync(
clientCredentials,
logger,
credentialsLoader,
credentialSourceLoaderParameters)
var credential = await credentialsProvider.GetCredentialAsync(
mergedOptions,
credentialSourceLoaderParameters,
cancellationToken)
.ConfigureAwait(false);

if (isTokenBinding)
Expand All @@ -52,7 +36,6 @@ public static async Task<ConfidentialClientApplicationBuilder> WithClientCredent
return builder.WithCertificate(credential.Certificate);
}

logger.LogError("A certificate, which is required for token binding, is missing in loaded credentials.");
throw new InvalidOperationException(IDWebErrorMessage.MissingTokenBindingCertificate);
}

Expand All @@ -74,101 +57,5 @@ public static async Task<ConfidentialClientApplicationBuilder> WithClientCredent

}
}

internal /* for test */ async static Task<CredentialDescription?> LoadCredentialForMsalOrFailAsync(
IEnumerable<CredentialDescription> clientCredentials,
ILogger logger,
ICredentialsLoader credentialsLoader,
CredentialSourceLoaderParameters? credentialSourceLoaderParameters)
{
string errorMessage = "\n";

foreach (CredentialDescription credential in clientCredentials)
{
Logger.AttemptToLoadCredentials(logger, credential);

if (!credential.Skip)
{
// Load the credentials and record error messages in case we need to fail at the end
try
{

await credentialsLoader.LoadCredentialsIfNeededAsync(credential, credentialSourceLoaderParameters);
}
catch (Exception ex)
{
Logger.AttemptToLoadCredentialsFailed(logger, credential, ex);
errorMessage += $"Credential {credential.Id} failed because: {ex} \n";
}


if (credential.CredentialType == CredentialType.SignedAssertion)
{
if (credential.SourceType == CredentialSource.SignedAssertionFromManagedIdentity)
{
if (credential.Skip)
{
Logger.NotUsingManagedIdentity(logger, errorMessage);
}
else
{
Logger.UsingManagedIdentity(logger);
return credential;
}
}
if (credential.SourceType == CredentialSource.SignedAssertionFilePath)
{
if (!credential.Skip)
{
Logger.UsingPodIdentityFile(logger, credential.SignedAssertionFileDiskPath ?? "not found");
return credential;
}
}
if (credential.SourceType == CredentialSource.SignedAssertionFromVault)
{
if (!credential.Skip)
{
Logger.UsingSignedAssertionFromVault(logger, credential.KeyVaultUrl ?? "undefined");
return credential;
}
}
if (credential.SourceType == CredentialSource.CustomSignedAssertion)
{
if (!credential.Skip)
{
Logger.UsingSignedAssertionFromCustomProvider(logger, credential.CustomSignedAssertionProviderName ?? "undefined");
return credential;
}
}
}

if (credential.CredentialType == CredentialType.Certificate)
{
if (credential.Certificate != null)
{
Logger.UsingCertThumbprint(logger, credential.Certificate?.Thumbprint);
return credential;
}
}

if (credential.CredentialType == CredentialType.Secret)
{
return credential;
}
}
}

if (clientCredentials.Any(c => c.CredentialType == CredentialType.Certificate || c.CredentialType == CredentialType.SignedAssertion))
{
throw new ArgumentException(
IDWebErrorMessage.ClientCertificatesHaveExpiredOrCannotBeLoaded + errorMessage,
nameof(clientCredentials));
}

logger.LogInformation($"No client credential could be used. Secret may have been defined elsewhere. " +
$"Count {(clientCredentials != null ? clientCredentials.Count() : 0)} ");

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace Microsoft.Identity.Web
{
internal partial class ConfidentialClientApplicationBuilderExtension
internal partial class CredentialsProvider
Comment thread
tlupes marked this conversation as resolved.
{
internal static class Logger
private class LogMessages
{
private static readonly Action<ILogger, string, Exception?> s_notManagedIdentity =
LoggerMessage.Define<string>(
Expand Down
Loading
Loading