Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
210 changes: 181 additions & 29 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 @@ -39,7 +39,7 @@ public TokenAcquisitionAspNetCore(
ILogger<TokenAcquisition> logger,
ITokenAcquisitionHost tokenAcquisitionHost,
IServiceProvider serviceProvider,
ICredentialsLoader credentialsLoader) :
ICredentialsProvider credentialsLoader) :
base(tokenCacheProvider, tokenAcquisitionHost, httpClientFactory, logger, serviceProvider, credentialsLoader)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
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)]
[Obsolete(IDWebErrorMessage.WithClientCredentialsIsObsolete, true)]
public static ConfidentialClientApplicationBuilder WithClientCredentials(
this ConfidentialClientApplicationBuilder builder,
IEnumerable<CredentialDescription> clientCredentials,
Expand All @@ -30,19 +32,42 @@ public static ConfidentialClientApplicationBuilder WithClientCredentials(
isTokenBinding: false).GetAwaiter().GetResult();
}

public static async Task<ConfidentialClientApplicationBuilder> WithClientCredentialsAsync(
[Obsolete(IDWebErrorMessage.WithClientCredentialsIsObsolete, true)]
public static Task<ConfidentialClientApplicationBuilder> WithClientCredentialsAsync(
this ConfidentialClientApplicationBuilder builder,
IEnumerable<CredentialDescription> clientCredentials,
ILogger logger,
ICredentialsLoader credentialsLoader,
CredentialSourceLoaderParameters? credentialSourceLoaderParameters,
bool isTokenBinding)
{
var credential = await LoadCredentialForMsalOrFailAsync(
clientCredentials,
logger,
credentialsLoader,
credentialSourceLoaderParameters)
MergedOptions mergedOptions = new MergedOptions
{
ClientCredentials = clientCredentials,
};

return WithClientCredentialsAsync(
builder,
mergedOptions,
new CredentialsProvider(new LogAdapter<CredentialsProvider>(logger), credentialsLoader, []),
credentialSourceLoaderParameters,
isTokenBinding,
CancellationToken.None);

}

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

if (isTokenBinding)
Expand All @@ -52,7 +77,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 @@ -75,100 +99,14 @@ public static async Task<ConfidentialClientApplicationBuilder> WithClientCredent
}
}

internal /* for test */ async static Task<CredentialDescription?> LoadCredentialForMsalOrFailAsync(
IEnumerable<CredentialDescription> clientCredentials,
ILogger logger,
ICredentialsLoader credentialsLoader,
CredentialSourceLoaderParameters? credentialSourceLoaderParameters)
// Used for backcompat support.
private class LogAdapter<TCategory>(ILogger innerLogger) : ILogger<TCategory>
{
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;
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => innerLogger.IsEnabled(logLevel);
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) =>
innerLogger.Log(logLevel, eventId, state, exception, formatter);
IDisposable ILogger.BeginScope<TState>(TState state) =>
innerLogger.BeginScope(state)!;
}
}
}
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this renamed?

{
internal static class Logger
private class LogMessages
{
private static readonly Action<ILogger, string, Exception?> s_notManagedIdentity =
LoggerMessage.Define<string>(
Expand Down
Loading