This commit is contained in:
Jonathan Myers 2020-11-13 15:17:18 -08:00
Родитель c9d3e73e34 115cb8ab1f
Коммит 1901798682
16 изменённых файлов: 230 добавлений и 60 удалений

Просмотреть файл

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CredentialProviderVersion>0.1.23</CredentialProviderVersion>
<CredentialProviderVersion>0.1.25</CredentialProviderVersion>
</PropertyGroup>
</Project>

Просмотреть файл

@ -12,7 +12,7 @@
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.0.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
<PackageReference Include="NuGet.Protocol" Version="5.3.0" />
<PackageReference Include="NuGet.Protocol" Version="5.6.0" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -42,6 +42,7 @@ namespace CredentialProvider.Microsoft.Tests.CredentialProviders.Vsts
{
Environment.SetEnvironmentVariable(EnvUtil.AuthorityEnvVar, string.Empty);
Environment.SetEnvironmentVariable(EnvUtil.MsalEnabledEnvVar, string.Empty);
Environment.SetEnvironmentVariable(EnvUtil.PpeHostsEnvVar, string.Empty);
}
[TestMethod]
@ -110,46 +111,69 @@ namespace CredentialProvider.Microsoft.Tests.CredentialProviders.Vsts
}
[TestMethod]
public async Task IsVstsUri_TenantHeaderNotPresent_ReturnsFalse()
public async Task GetFeedUriSource_TenantHeaderNotPresent_ReturnsExternal()
{
var requestUri = new Uri("https://example.pkgs.visualstudio.com/_packaging/feed/nuget/v3/index.json");
var isVstsUri = await authUtil.IsVstsUriAsync(requestUri);
isVstsUri.Should().BeFalse();
var feedSource = await authUtil.GetAzDevDeploymentType(requestUri);
feedSource.Should().Be(AzDevDeploymentType.External);
}
[TestMethod]
public async Task IsVstsUri_TenantHeaderPresent_ReturnsFalse()
public async Task GetFeedUriSource_TenantHeaderPresent_VssAuthorizationEndpointNotPresent_ReturnsExternal()
{
var requestUri = new Uri("https://example.pkgs.visualstudio.com/_packaging/feed/nuget/v3/index.json");
MockVssResourceTenantHeader();
var isVstsUri = await authUtil.IsVstsUriAsync(requestUri);
isVstsUri.Should().BeFalse();
var feedSource = await authUtil.GetAzDevDeploymentType(requestUri);
feedSource.Should().Be(AzDevDeploymentType.External);
}
[TestMethod]
public async Task IsVstsUri_AuthorizationEndpointHeaderPresent_ReturnsFalse()
public async Task GetFeedUriSource_AuthorizationEndpointHeaderPresent_TenantHeaderNotPresent_ReturnsExternal()
{
var requestUri = new Uri("https://example.pkgs.visualstudio.com/_packaging/feed/nuget/v3/index.json");
MockVssAuthorizationEndpointHeader();
var isVstsUri = await authUtil.IsVstsUriAsync(requestUri);
isVstsUri.Should().BeFalse();
var feedSource = await authUtil.GetAzDevDeploymentType(requestUri);
feedSource.Should().Be(AzDevDeploymentType.External);
}
[TestMethod]
public async Task IsVstsUri_BothHeadersPresent_ReturnsTrue()
public async Task GetFeedUriSource_BothHeadersPresent_ReturnsHosted()
{
var requestUri = new Uri("https://example.pkgs.visualstudio.com/_packaging/feed/nuget/v3/index.json");
MockVssResourceTenantHeader();
MockVssAuthorizationEndpointHeader();
var isVstsUri = await authUtil.IsVstsUriAsync(requestUri);
isVstsUri.Should().BeTrue();
var feedSource = await authUtil.GetAzDevDeploymentType(requestUri);
feedSource.Should().Be(AzDevDeploymentType.Hosted);
}
[TestMethod]
public async Task GetFeedUriSource_NoHttps_ReturnsExternal()
{
var requestUri = new Uri("http://example.pkgs.visualstudio.com/_packaging/feed/nuget/v3/index.json");
MockVssResourceTenantHeader();
MockVssAuthorizationEndpointHeader();
var feedSource = await authUtil.GetAzDevDeploymentType(requestUri);
feedSource.Should().Be(AzDevDeploymentType.External);
}
[TestMethod]
public async Task GetFeedUriSource_OnPremHeaderPresent_ReturnsOnPrem()
{
var requestUri = new Uri("https://example.pkgs.visualstudio.com/_packaging/feed/nuget/v3/index.json");
MockResponseHeaders(AuthUtil.VssE2EID, "id");
var feedSource = await authUtil.GetAzDevDeploymentType(requestUri);
feedSource.Should().Be(AzDevDeploymentType.OnPrem);
}
[TestMethod]

Просмотреть файл

@ -82,7 +82,7 @@ namespace CredentialProvider.Microsoft.Tests.CredentialProviders.Vsts
}
mockAuthUtil
.Verify(x => x.IsVstsUriAsync(It.IsAny<Uri>()), Times.Never, "because we shouldn't probe for known sources");
.Verify(x => x.GetAzDevDeploymentType(It.IsAny<Uri>()), Times.Never, "because we shouldn't probe for known sources");
}
[TestMethod]
@ -103,7 +103,29 @@ namespace CredentialProvider.Microsoft.Tests.CredentialProviders.Vsts
}
mockAuthUtil
.Verify(x => x.IsVstsUriAsync(It.IsAny<Uri>()), Times.Never, "because we shouldn't probe for known sources");
.Verify(x => x.GetAzDevDeploymentType(It.IsAny<Uri>()), Times.Never, "because we shouldn't probe for known sources");
Environment.SetEnvironmentVariable(EnvUtil.SupportedHostsEnvVar, string.Empty);
}
[TestMethod]
public async Task CanProvideCredentials_CallsGetFeedUriSourceWhenSourcesAreInvalid()
{
var sources = new[]
{
new Uri(@"http://example.overrideOne.com/_packaging/TestFeed/nuget/v3/index.json"),
new Uri(@"https://example.overrideTwo.com/_packaging/TestFeed/nuget/v3/index.json"),
new Uri(@"https://example.overrideThre.com/_packaging/TestFeed/nuget/v3/index.json"),
};
foreach (var source in sources)
{
var canProvideCredentials = await vstsCredentialProvider.CanProvideCredentialsAsync(source);
canProvideCredentials.Should().BeFalse($"because {source} is not a valid host");
}
mockAuthUtil
.Verify(x => x.GetAzDevDeploymentType(It.IsAny<Uri>()), Times.Exactly(3), "because sources were unknown");
}
[TestMethod]

Просмотреть файл

@ -35,7 +35,7 @@
<PackageReference Include="Microsoft.Identity.Client" Version="4.11.0" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.8.0-preview" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.3" />
<PackageReference Include="NuGet.Protocol" Version="5.3.0" />
<PackageReference Include="NuGet.Protocol" Version="5.6.0" />
<PackageReference Include="PowerArgs" Version="3.6.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19554-01" PrivateAssets="All"/>
</ItemGroup>

Просмотреть файл

@ -70,10 +70,12 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
public class UserInterfaceBearerTokenProvider : IBearerTokenProvider
{
private readonly IAdalTokenProvider adalTokenProvider;
private readonly ILogger logger;
public UserInterfaceBearerTokenProvider(IAdalTokenProvider adalTokenProvider)
public UserInterfaceBearerTokenProvider(IAdalTokenProvider adalTokenProvider, ILogger logger)
{
this.adalTokenProvider = adalTokenProvider;
this.adalTokenProvider = adalTokenProvider;
this.logger = logger;
}
public bool Interactive { get; } = true;
@ -81,6 +83,7 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
public async Task<string> GetTokenAsync(Uri uri, CancellationToken cancellationToken)
{
logger.Minimal(string.Format(Resources.UIFlowStarted, this.Name, uri.AbsoluteUri));
return (await adalTokenProvider.AcquireTokenWithUI(cancellationToken))?.AccessToken;
}

Просмотреть файл

@ -26,7 +26,7 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
// Order here is important - providers (potentially) run in this order.
new AdalCacheBearerTokenProvider(adalTokenProvider),
new WindowsIntegratedAuthBearerTokenProvider(adalTokenProvider),
new UserInterfaceBearerTokenProvider(adalTokenProvider),
new UserInterfaceBearerTokenProvider(adalTokenProvider, logger),
new DeviceCodeFlowBearerTokenProvider(adalTokenProvider, logger)
};
}

Просмотреть файл

@ -17,18 +17,26 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
{
Task<Uri> GetAadAuthorityUriAsync(Uri uri, CancellationToken cancellationToken);
Task<bool> IsVstsUriAsync(Uri uri);
Task<AzDevDeploymentType> GetAzDevDeploymentType(Uri uri);
Task<Uri> GetAuthorizationEndpoint(Uri uri, CancellationToken cancellationToken);
}
public enum AzDevDeploymentType
{
External,
Hosted,
OnPrem
}
public class AuthUtil : IAuthUtil
{
public const string VssResourceTenant = "X-VSS-ResourceTenant";
public const string VssAuthorizationEndpoint = "X-VSS-AuthorizationEndpoint";
private const string OrganizationsTenant = "organizations";
private const string CommonTenant = "common";
public const string VssE2EID = "X-VSS-E2EID";
private readonly ILogger logger;
public AuthUtil(ILogger logger)
@ -46,7 +54,7 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
var headers = await GetResponseHeadersAsync(uri, cancellationToken);
var bearerHeaders = headers.WwwAuthenticate.Where(x => x.Scheme.Equals("Bearer", StringComparison.Ordinal));
foreach (var param in bearerHeaders)
{
if (param.Parameter == null)
@ -77,17 +85,25 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
return new Uri($"{aadBase}/{tenant}");
}
public async Task<bool> IsVstsUriAsync(Uri uri)
public async Task<AzDevDeploymentType> GetAzDevDeploymentType(Uri uri)
{
if (!IsValidScheme(uri))
{
// We are not talking to a https endpoint so it cannot be a VSTS endpoint
return false;
}
// Ping the url to see from headers whether it's an Azure Artifacts feed or external
var responseHeaders = await GetResponseHeadersAsync(uri, cancellationToken: default);
return responseHeaders.Contains(VssResourceTenant) && responseHeaders.Contains(VssAuthorizationEndpoint);
// Hosted only allows https
if (IsHttpsScheme(uri) && responseHeaders.Contains(VssResourceTenant) && responseHeaders.Contains(VssAuthorizationEndpoint))
{
return AzDevDeploymentType.Hosted;
}
// If not hosted and has E2EID, assume on prem.
if (responseHeaders.Contains(VssE2EID))
{
return AzDevDeploymentType.OnPrem;
}
// Assume uri is from an external source if expected headers aren't present.
return AzDevDeploymentType.External;
}
public async Task<Uri> GetAuthorizationEndpoint(Uri uri, CancellationToken cancellationToken)
@ -145,7 +161,7 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
return ppeHosts.Any(host => uri.Host.EndsWith(host, StringComparison.OrdinalIgnoreCase));
}
private bool IsValidScheme(Uri uri)
private bool IsHttpsScheme(Uri uri)
{
try
{

Просмотреть файл

@ -49,16 +49,37 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
return false;
}
var validHosts = EnvUtil.GetHostsFromEnvironment(Logger, EnvUtil.SupportedHostsEnvVar, new[]
{
".pkgs.vsts.me", // DevFabric
".pkgs.codedev.ms", // DevFabric
".pkgs.codeapp.ms", // AppFabric
".pkgs.visualstudio.com", // Prod
".pkgs.dev.azure.com" // Prod
});
var validHosts = EnvUtil.GetHostsFromEnvironment(Logger, EnvUtil.SupportedHostsEnvVar, new[]
{
".pkgs.vsts.me", // DevFabric
".pkgs.codedev.ms", // DevFabric
".pkgs.codeapp.ms", // AppFabric
".pkgs.visualstudio.com", // Prod
".pkgs.dev.azure.com" // Prod
});
return validHosts.Any(host => uri.Host.EndsWith(host, StringComparison.OrdinalIgnoreCase)) || await authUtil.IsVstsUriAsync(uri);
bool isValidHost = validHosts.Any(host => uri.Host.EndsWith(host, StringComparison.OrdinalIgnoreCase));
if (isValidHost)
{
Verbose(string.Format(Resources.HostAccepted, uri.Host));
return true;
}
var azDevOpsType = await authUtil.GetAzDevDeploymentType(uri);
if (azDevOpsType == AzDevDeploymentType.Hosted)
{
Verbose(Resources.ValidHeaders);
return true;
}
if (azDevOpsType == AzDevDeploymentType.OnPrem)
{
Verbose(Resources.OnPremDetected);
return false;
}
Verbose(string.Format(Resources.ExternalUri, uri));
return false;
}
public override async Task<GetAuthenticationCredentialsResponse> HandleRequestAsync(GetAuthenticationCredentialsRequest request, CancellationToken cancellationToken)
@ -115,7 +136,7 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
if (!string.IsNullOrWhiteSpace(sessionToken))
{
Verbose(string.Format(Resources.VSTSSessionTokenCreated, request.Uri.ToString()));
Verbose(string.Format(Resources.VSTSSessionTokenCreated, request.Uri.AbsoluteUri));
return new GetAuthenticationCredentialsResponse(
Username,
sessionToken,
@ -126,11 +147,11 @@ namespace NuGetCredentialProvider.CredentialProviders.Vsts
}
catch (Exception e)
{
Verbose(string.Format(Resources.VSTSCreateSessionException, request.Uri, e.Message, e.StackTrace));
Verbose(string.Format(Resources.VSTSCreateSessionException, request.Uri.AbsoluteUri, e.Message, e.StackTrace));
}
}
Verbose(string.Format(Resources.VSTSCredentialsNotFound, request.Uri.ToString()));
Verbose(string.Format(Resources.VSTSCredentialsNotFound, request.Uri.AbsoluteUri));
return null;
}
}

Просмотреть файл

@ -58,7 +58,7 @@ namespace NuGetCredentialProvider.CredentialProviders.VstsBuildTask
Verbose($"{uriPrefix}");
}
string uriString = request.Uri.ToString();
string uriString = request.Uri.AbsoluteUri;
string matchedPrefix = uriPrefixes.FirstOrDefault(prefix => uriString.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
Verbose(string.Format(Resources.BuildTaskMatchedPrefix, matchedPrefix != null ? matchedPrefix : Resources.BuildTaskNoMatchingPrefixes));

Просмотреть файл

@ -67,7 +67,7 @@ namespace NuGetCredentialProvider.CredentialProviders.VstsBuildTaskServiceEndpoi
Verbose(string.Format(Resources.IsRetry, request.IsRetry));
string uriString = request.Uri.ToString();
string uriString = request.Uri.AbsoluteUri;
bool endpointFound = Credentials.TryGetValue(uriString, out EndpointCredentials matchingEndpoint);
if (endpointFound)
{

Просмотреть файл

@ -44,7 +44,7 @@ namespace NuGetCredentialProvider.RequestHandlers
public override async Task<GetAuthenticationCredentialsResponse> HandleRequestAsync(GetAuthenticationCredentialsRequest request)
{
Logger.Verbose(string.Format(Resources.HandlingAuthRequest, request.Uri, request.IsRetry, request.IsNonInteractive, request.CanShowDialog));
Logger.Verbose(string.Format(Resources.HandlingAuthRequest, request.Uri.AbsoluteUri, request.IsRetry, request.IsNonInteractive, request.CanShowDialog));
if (request?.Uri == null)
{
@ -57,15 +57,16 @@ namespace NuGetCredentialProvider.RequestHandlers
responseCode: MessageResponseCode.Error);
}
Logger.Verbose(string.Format(Resources.Uri, request.Uri));
Logger.Verbose(string.Format(Resources.Uri, request.Uri.AbsoluteUri));
foreach (ICredentialProvider credentialProvider in credentialProviders)
{
if (await credentialProvider.CanProvideCredentialsAsync(request.Uri) == false)
{
Logger.Verbose(string.Format(Resources.SkippingCredentialProvider, credentialProvider, request.Uri.ToString()));
Logger.Verbose(string.Format(Resources.SkippingCredentialProvider, credentialProvider, request.Uri.AbsoluteUri));
continue;
}
}
Logger.Verbose(string.Format(Resources.UsingCredentialProvider, credentialProvider, request.Uri.AbsoluteUri));
if (credentialProvider.IsCachable && TryCache(request, out string cachedToken))
{
@ -84,7 +85,7 @@ namespace NuGetCredentialProvider.RequestHandlers
{
if (cache != null && credentialProvider.IsCachable)
{
Logger.Verbose(string.Format(Resources.CachingSessionToken, request.Uri.ToString()));
Logger.Verbose(string.Format(Resources.CachingSessionToken, request.Uri.AbsoluteUri));
cache[request.Uri] = response.Password;
}
@ -142,18 +143,18 @@ namespace NuGetCredentialProvider.RequestHandlers
Logger.Verbose(string.Format(Resources.IsRetry, request.IsRetry));
if (request.IsRetry)
{
Logger.Verbose(string.Format(Resources.InvalidatingCachedSessionToken, request.Uri.ToString()));
Logger.Verbose(string.Format(Resources.InvalidatingCachedSessionToken, request.Uri.AbsoluteUri));
cache?.Remove(request.Uri);
return false;
}
else if (cache.TryGetValue(request.Uri, out string password))
{
Logger.Verbose(string.Format(Resources.FoundCachedSessionToken, request.Uri.ToString()));
Logger.Verbose(string.Format(Resources.FoundCachedSessionToken, request.Uri.AbsoluteUri));
cachedToken = password;
return true;
}
Logger.Verbose(string.Format(Resources.CachedSessionTokenNotFound, request.Uri.ToString()));
Logger.Verbose(string.Format(Resources.CachedSessionTokenNotFound, request.Uri.AbsoluteUri));
return false;
}
}

54
CredentialProvider.Microsoft/Resources.Designer.cs сгенерированный
Просмотреть файл

@ -442,6 +442,15 @@ namespace NuGetCredentialProvider {
}
}
/// <summary>
/// Looks up a localized string similar to {0} is not an Azure Artifacts feed..
/// </summary>
internal static string ExternalUri {
get {
return ResourceManager.GetString("ExternalUri", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Faulted on message: {0}.
/// </summary>
@ -505,6 +514,15 @@ namespace NuGetCredentialProvider {
}
}
/// <summary>
/// Looks up a localized string similar to Matched well-known Azure DevOps Service hostname: {0}.
/// </summary>
internal static string HostAccepted {
get {
return ResourceManager.GetString("HostAccepted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalidating SessionToken cache for {0}.
/// </summary>
@ -559,6 +577,15 @@ namespace NuGetCredentialProvider {
}
}
/// <summary>
/// Looks up a localized string similar to Detected an on premise Azure DevOps Server..
/// </summary>
internal static string OnPremDetected {
get {
return ResourceManager.GetString("OnPremDetected", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Parsing json.
/// </summary>
@ -739,6 +766,15 @@ namespace NuGetCredentialProvider {
}
}
/// <summary>
/// Looks up a localized string similar to Using the {0} flow for uri {1}. User sign-in required in a pop-up authentication window..
/// </summary>
internal static string UIFlowStarted {
get {
return ResourceManager.GetString("UIFlowStarted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to URI: {0}.
/// </summary>
@ -757,6 +793,24 @@ namespace NuGetCredentialProvider {
}
}
/// <summary>
/// Looks up a localized string similar to Using {0} to try to get credentials for {1}..
/// </summary>
internal static string UsingCredentialProvider {
get {
return ResourceManager.GetString("UsingCredentialProvider", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Detected a hosted Azure DevOps Service..
/// </summary>
internal static string ValidHeaders {
get {
return ResourceManager.GetString("ValidHeaders", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error happened while parsing json: {0}.
/// </summary>

Просмотреть файл

@ -476,4 +476,23 @@ Provide MSAL Cache Location
<data name="ForcingCanShowDialogFromTo" xml:space="preserve">
<value>Forcing canShowDialog from {0} to {1} due to setting from environment variable</value>
</data>
<data name="UIFlowStarted" xml:space="preserve">
<value>Using the {0} flow for uri {1}. User sign-in required in a pop-up authentication window.</value>
</data>
<data name="HostAccepted" xml:space="preserve">
<value>Matched well-known Azure DevOps Service hostname: {0}</value>
</data>
<data name="OnPremDetected" xml:space="preserve">
<value>Detected an on premise Azure DevOps Server.</value>
</data>
<data name="UsingCredentialProvider" xml:space="preserve">
<value>Using {0} to try to get credentials for {1}.</value>
<comment>{0} - credential provider name, {1} - Uri</comment>
</data>
<data name="ValidHeaders" xml:space="preserve">
<value>Detected a hosted Azure DevOps Service.</value>
</data>
<data name="ExternalUri" xml:space="preserve">
<value>{0} is not an Azure Artifacts feed.</value>
</data>
</root>

Просмотреть файл

@ -38,7 +38,9 @@ Dotnet needs the `netcore` version to be installed. NuGet and MSBuild need the `
[PowerShell helper script](helpers/installcredprovider.ps1)
- To install netcore, run `installcredprovider.ps1`
- e.g. `iex "& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) }"`
- To install both netfx and netcore, run `installcredprovider.ps1 -AddNetfx`. The netfx version is needed for nuget.exe.
- e.g. `iex "& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) } -AddNetfx"`
#### Manual installation on Windows
@ -53,12 +55,13 @@ Using the above is recommended, but as per [NuGet's plugin discovery rules](http
#### Automatic bash script
[Linux or Mac helper script](helpers/installcredprovider.sh)
- e.g. `wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash`
#### Manual installation on Linux and Mac
1. Download the latest release of [Microsoft.NuGet.CredentialProvider.tar.gz](https://github.com/Microsoft/artifacts-credprovider/releases)
2. Untar the file
3. Copy the `netcore` directory from the extracted archive to `$HOME\.nuget\plugins`
3. Copy the `netcore` directory from the extracted archive to `$HOME/.nuget/plugins`
Using the above is recommended, but as per [NuGet's plugin discovery rules](https://github.com/NuGet/Home/wiki/NuGet-cross-plat-authentication-plugin#plugin-installation-and-discovery), alternatively you can install the credential provider to a location you prefer, and then set the environment variable NUGET_PLUGIN_PATHS to the .dll of the credential provider found in plugins\netcore\CredentialProvider.Microsoft\CredentialProvider.Microsoft.dll. For example, $env:NUGET_PLUGIN_PATHS="my-alternative-location\CredentialProvider.Microsoft.dll".
@ -105,12 +108,19 @@ Once you've successfully acquired a token, you can run authenticated commands wi
### Unattended build agents
With Azure DevOps Pipelines, please use the [NuGet Authenticate](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/package/nuget-authenticate?view=azure-devops) task before running NuGet, dotnet or MSBuild commands that need authentication.
#### Azure DevOps Pipelines
Use the [NuGet Authenticate](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/package/nuget-authenticate?view=azure-devops) task before running NuGet, dotnet or MSBuild commands that need authentication.
#### Other automated build scenarios
If you're running the command as part of an automated build on an unattended build agent outside of Azure DevOps Pipelines, you can supply an access token directly using the `VSS_NUGET_EXTERNAL_FEED_ENDPOINTS` [environment variable](#environment-variables). The use of [Personal Access Tokens](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops) is recommended. You may need to restart the agent service or the computer before the environment variables are available to the agent.
### Docker containers
[Sample Dockerfile](https://github.com/microsoft/artifacts-credprovider/blob/master/samples/dockerfile.sample.txt)
[Managing NuGet credentials in Docker scenarios](https://github.com/dotnet/dotnet-docker/blob/master/documentation/scenarios/nuget-credentials.md#using-the-azure-artifact-credential-provider)
### Azure DevOps Server
The Azure Artifacts Credential Provider may not be necessary for an on-premises Azure DevOps Server on Windows. If the credential provider is needed, it cannot acquire credentials interactively, therefore, the VSS_NUGET_EXTERNAL_FEED_ENDPOINTS environment variable must be used as an alternative. Supply a [Personal Access Token](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops) directly using the `VSS_NUGET_EXTERNAL_FEED_ENDPOINTS` [environment variable](#environment-variables).
From Azure DevOps Server 2020 RC1 forward, the NuGet Authenticate task can be used in Pipelines.
## Session Token Cache Locations

Просмотреть файл

@ -4,13 +4,13 @@ ARG FEED_URL
ARG PAT
# download and install latest credential provider. Not required after https://github.com/dotnet/dotnet-docker/issues/878
RUN wget -qO- https://raw.githubusercontent.com/Microsoft/artifacts-credprovider/master/helpers/installcredprovider.sh | bash
RUN wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash
# Optional
WORKDIR /workdir
COPY ./ .
# Optional: Sometimes the http client hangs because of a .NEt issue. Setting this in dockerfile helps
# Optional: Sometimes the http client hangs because of a .NET issue. Setting this in dockerfile helps
ENV DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0
# Environment variable to enable seesion token cache. More on this here: https://github.com/Microsoft/artifacts-credprovider#help
@ -25,4 +25,4 @@ ENV VSS_NUGET_EXTERNAL_FEED_ENDPOINTS {\"endpointCredentials\": [{\"endpoint\":\
RUN dotnet restore
# Optional: Extended step to build the app using dotnet msbuild.
RUN dotnet build dirs.proj
RUN dotnet build dirs.proj