Add UserAgent and IMsalHttpClientFactory support to authentication library (#404)
Bringing PlatformUtils and UserAgent defaults over to the authentication library. Since netstandard2.0 doesn't have all the types required it's still expected that callers will need to construct their own http client factory. For .NET Framework however, using a static HttpClient is still the recommendation so providing some extension methods to support that.
This commit is contained in:
Родитель
f5a44bd497
Коммит
9abad78d08
|
@ -35,7 +35,7 @@ public class AccountPriorityTests
|
|||
{
|
||||
Username = "bill.gates@live.com",
|
||||
Environment = string.Empty,
|
||||
HomeAccountId = new AccountId(string.Empty, string.Empty, Constants.MsaAccountTenant.ToString()),
|
||||
HomeAccountId = new AccountId(string.Empty, string.Empty, MsalConstants.MsaAccountTenant.ToString()),
|
||||
};
|
||||
|
||||
private static readonly List<List<IAccount>> Permutations = new List<List<IAccount>>()
|
||||
|
@ -53,7 +53,7 @@ public class AccountPriorityTests
|
|||
{
|
||||
foreach (var accounts in Permutations)
|
||||
{
|
||||
var applicable = MsalExtensions.GetApplicableAccounts(accounts, Constants.FirstPartyTenant, loginHint: null);
|
||||
var applicable = MsalExtensions.GetApplicableAccounts(accounts, MsalConstants.FirstPartyTenant, loginHint: null);
|
||||
Assert.AreEqual(applicable[0].Item1.Username, MsaUser.Username);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,12 @@ public static class AzureArtifacts
|
|||
return builder;
|
||||
}
|
||||
|
||||
public static PublicClientApplicationBuilder WithHttpClient(this PublicClientApplicationBuilder builder, HttpClient? httpClient = null)
|
||||
{
|
||||
// Default HttpClient is only meant for .NET Framework clients that can't use the SocketsHttpHandler
|
||||
return builder.WithHttpClientFactory(new MsalHttpClientFactory(httpClient ?? new HttpClient()));
|
||||
}
|
||||
|
||||
#region https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/3590
|
||||
enum GetAncestorFlags
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.1.0$(VersionSuffix)</Version>
|
||||
<Version>0.1.1$(VersionSuffix)</Version>
|
||||
<Authors>Microsoft</Authors>
|
||||
<Owners>Microsoft</Owners>
|
||||
<Description>Azure Artifacts authentication library for credential providers.</Description>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace Microsoft.Artifacts.Authentication;
|
||||
|
||||
public static class Constants
|
||||
public static class MsalConstants
|
||||
{
|
||||
private const string AzureDevOpsResource = "499b84ac-1321-427f-aa17-267ca6975798/.default";
|
||||
public static readonly IEnumerable<string> AzureDevOpsScopes = Array.AsReadOnly(new[] { AzureDevOpsResource });
|
|
@ -34,7 +34,7 @@ public class MsalDeviceCodeTokenProvider : ITokenProvider
|
|||
|
||||
try
|
||||
{
|
||||
var result = await app.AcquireTokenWithDeviceCode(Constants.AzureDevOpsScopes, tokenRequest.DeviceCodeResultCallback ?? ((DeviceCodeResult deviceCodeResult) =>
|
||||
var result = await app.AcquireTokenWithDeviceCode(MsalConstants.AzureDevOpsScopes, tokenRequest.DeviceCodeResultCallback ?? ((DeviceCodeResult deviceCodeResult) =>
|
||||
{
|
||||
logger.LogInformation(deviceCodeResult.Message);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public static partial class MsalExtensions
|
|||
{
|
||||
applicableAccounts.Add((account, canonicalName));
|
||||
}
|
||||
else if (accountTenantId == Constants.MsaAccountTenant && (authorityTenantId == Constants.FirstPartyTenant || authorityTenantId == Guid.Empty))
|
||||
else if (accountTenantId == MsalConstants.MsaAccountTenant && (authorityTenantId == MsalConstants.FirstPartyTenant || authorityTenantId == Guid.Empty))
|
||||
{
|
||||
applicableAccounts.Add((account, canonicalName));
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ public static partial class MsalExtensions
|
|||
// Even if using the organizations tenant the presence of an MSA will attempt to use the consumers tenant
|
||||
// which is not supported by the Azure DevOps application. Detect this case and use the first party tenant.
|
||||
|
||||
if (Guid.TryParse(account.HomeAccountId?.TenantId, out Guid accountTenantId) && accountTenantId == Constants.MsaAccountTenant)
|
||||
if (Guid.TryParse(account.HomeAccountId?.TenantId, out Guid accountTenantId) && accountTenantId == MsalConstants.MsaAccountTenant)
|
||||
{
|
||||
builder = builder.WithTenantId(Constants.FirstPartyTenant.ToString());
|
||||
builder = builder.WithTenantId(MsalConstants.FirstPartyTenant.ToString());
|
||||
}
|
||||
|
||||
return builder;
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Net.Http.Headers;
|
||||
using Microsoft.Identity.Client;
|
||||
|
||||
namespace Microsoft.Artifacts.Authentication;
|
||||
|
||||
public class MsalHttpClientFactory : IMsalHttpClientFactory
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
public MsalHttpClientFactory(HttpClient httpClient)
|
||||
{
|
||||
this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
|
||||
var userAgent = this.httpClient.DefaultRequestHeaders.UserAgent;
|
||||
userAgent.Add(ProgramProduct);
|
||||
userAgent.Add(ProgramComment);
|
||||
userAgent.Add(ClrProduct);
|
||||
userAgent.Add(ClrComment);
|
||||
}
|
||||
|
||||
public static ProductInfoHeaderValue ProgramProduct =>
|
||||
new ProductInfoHeaderValue(PlatformInformation.GetProgramName(), PlatformInformation.GetProgramVersion());
|
||||
|
||||
public static ProductInfoHeaderValue ProgramComment =>
|
||||
new ProductInfoHeaderValue($"({PlatformInformation.GetOSType()}; {PlatformInformation.GetCpuArchitecture()}; {PlatformInformation.GetOsDescription()})");
|
||||
|
||||
public static ProductInfoHeaderValue ClrProduct =>
|
||||
new ProductInfoHeaderValue("CLR", PlatformInformation.GetClrVersion());
|
||||
|
||||
public static ProductInfoHeaderValue ClrComment =>
|
||||
new ProductInfoHeaderValue($"({PlatformInformation.GetClrFramework()}; {PlatformInformation.GetClrRuntime()}; {PlatformInformation.GetClrDescription()})");
|
||||
|
||||
// Produces a value similar to the following:
|
||||
// CredentialProvider.Microsoft/1.0.4+aae4981de95d543b7935811c05474e393dd9e144 (Windows; X64; Microsoft Windows 10.0.19045) CLR/6.0.16 (.NETCoreApp,Version=v6.0; win10-x64; .NET 6.0.16)
|
||||
public static IEnumerable<ProductInfoHeaderValue> UserAgent =>
|
||||
Array.AsReadOnly(new[]
|
||||
{
|
||||
ProgramProduct,
|
||||
ProgramComment,
|
||||
ClrProduct,
|
||||
ClrComment
|
||||
});
|
||||
|
||||
public HttpClient GetHttpClient()
|
||||
{
|
||||
return httpClient;
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ public class MsalIntegratedWindowsAuthTokenProvider : ITokenProvider
|
|||
return null;
|
||||
}
|
||||
|
||||
var result = await app.AcquireTokenByIntegratedWindowsAuth(Constants.AzureDevOpsScopes)
|
||||
var result = await app.AcquireTokenByIntegratedWindowsAuth(MsalConstants.AzureDevOpsScopes)
|
||||
.WithUsername(upn)
|
||||
.ExecuteAsync(cancellationToken);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public class MsalInteractiveTokenProvider : ITokenProvider
|
|||
|
||||
try
|
||||
{
|
||||
var result = await app.AcquireTokenInteractive(Constants.AzureDevOpsScopes)
|
||||
var result = await app.AcquireTokenInteractive(MsalConstants.AzureDevOpsScopes)
|
||||
.WithPrompt(Prompt.SelectAccount)
|
||||
.WithUseEmbeddedWebView(false)
|
||||
.ExecuteAsync(cts.Token);
|
||||
|
|
|
@ -61,7 +61,7 @@ public class MsalSilentTokenProvider : ITokenProvider
|
|||
{
|
||||
this.logger.LogTrace(Resources.MsalAccountAttempt, canonicalName);
|
||||
|
||||
var result = await app.AcquireTokenSilent(Constants.AzureDevOpsScopes, account)
|
||||
var result = await app.AcquireTokenSilent(MsalConstants.AzureDevOpsScopes, account)
|
||||
.WithAccountTenantId(account)
|
||||
.ExecuteAsync(cancellationToken);
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Artifacts.Authentication;
|
||||
|
||||
public static class PlatformInformation
|
||||
{
|
||||
private static string? programName = null;
|
||||
private static string? programVersion = null;
|
||||
private static string? runtimeIdentifier = null;
|
||||
|
||||
private static AssemblyName CurrentAssemblyName => typeof(PlatformInformation).Assembly.GetName();
|
||||
|
||||
public static string GetProgramName()
|
||||
{
|
||||
return programName ??= Assembly
|
||||
.GetEntryAssembly()?
|
||||
.GetCustomAttribute<AssemblyTitleAttribute>()?.Title ?? CurrentAssemblyName.Name;
|
||||
}
|
||||
|
||||
public static string GetProgramVersion()
|
||||
{
|
||||
return programVersion ??= Assembly
|
||||
.GetEntryAssembly()?
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? CurrentAssemblyName.Version.ToString();
|
||||
}
|
||||
|
||||
public static string GetOSType()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return nameof(OSPlatform.Windows);
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return nameof(OSPlatform.Linux);
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return nameof(OSPlatform.OSX);
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
public static string GetCpuArchitecture()
|
||||
{
|
||||
return RuntimeInformation.OSArchitecture.ToString();
|
||||
}
|
||||
|
||||
public static string GetOsDescription()
|
||||
{
|
||||
return RuntimeInformation.OSDescription;
|
||||
}
|
||||
|
||||
public static string GetClrVersion()
|
||||
{
|
||||
return Environment.Version.ToString();
|
||||
}
|
||||
|
||||
public static string GetClrFramework()
|
||||
{
|
||||
return AppContext.TargetFrameworkName;
|
||||
}
|
||||
|
||||
public static string GetClrRuntime()
|
||||
{
|
||||
// RuntimeInformation.RuntimeIdentifier not available on .NET Standard
|
||||
return runtimeIdentifier ??= AppContext.GetData("RUNTIME_IDENTIFIER") as string ?? "win-x64";
|
||||
}
|
||||
|
||||
public static string GetClrDescription()
|
||||
{
|
||||
return RuntimeInformation.FrameworkDescription;
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче