PSResourceGet
PSResourceGet copied to clipboard
PAT authentication failing with Azure Devops on premise Artifacts feed
Prerequisites
- [X] Write a descriptive title.
- [X] Make sure you are able to repro it on the latest released version
- [X] Search the existing issues.
Steps to reproduce
Access to onprem Azure Devops Artifacts feed failed when authenticating using a PAT token, created by Azure Devops.
$Token = "...TOKEN..." | ConvertTo-SecureString -AsPlainText -Force $Credential = New-Object System.Management.Automation.PSCredential('PAT', $Token) Set-Secret -Name AzureArtifactToken -Secret $Credential $CredentialInfo = New-Object Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo ("MySecrets", "AzureArtifactToken")
$Params = @{ Name = 'RepoName' Uri = 'https://mydomain.org/org/grp/_packaging/grp/nuget/v3/index.json' Trusted = $true CredentialInfo = $CredentialInfo ApiVersion = 'v3' } Set-PSResourceRepository @Params
After examine this issue further, we found out that the API server calls were performed by a HttpClient class method, using 'negotiate' authentication by default (in our situation). As the PAT token in the SecretStore is retreived as a PSCredential, PSResourceGet passes this PAT token as a 'NetworkCredential' to the ServerApiCalls class methods (incl. v3).
HttpClientHandler handler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
Credentials = networkCredential
};
But, a PAT token requires 'Basic authentication' to succeed. See https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows#use-a-pat-in-your-code. To test this, I modified the API calls (incl. V3) with a token detection (username = 'PAT') and added a HttpClient AuthenticationHeader with Basic authentication. After rebuilding and testing this code, the Authentication was successful!
public ServerApiCall(PSRepositoryInfo repository, NetworkCredential networkCredential)
{
this.Repository = repository;
HttpClientHandler handler = new HttpClientHandler();
bool token = false;
if(networkCredential != null)
{
if (String.Equals("PAT", networkCredential.UserName))
{
token = true;
}
};
if (token)
{
string credString = string.Format(":{0}", networkCredential.Password);
byte[] byteArray = Encoding.ASCII.GetBytes(credString);
_sessionClient = new HttpClient(handler);
_sessionClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
} else {
handler.Credentials = networkCredential;
_sessionClient = new HttpClient(handler);
};
}
Expected behavior
Find-PSResource -> list of requested packages
Actual behavior
Find-PSResource: 'Response status code does not indicate success: 401 (Unauthorized).' Re-run the command with -Credential.
Error details
No response
Environment data
ModuleType Version PreRelease Name ExportedCommands
---------- ------- ---------- ---- ----------------
Binary 1.0.2 Microsoft.PowerShell.PSResourceGet {Find-PSResource, Get-InstalledPSResource, Get-PSResourceRepository, Get-PSScriptFileInfo…}
Key : PSVersion
Value : 7.3.2
Name : PSVersion
Key : PSEdition
Value : Core
Name : PSEdition
Key : GitCommitId
Value : 7.3.2
Name : GitCommitId
Key : OS
Value : Microsoft Windows 10.0.14393
Name : OS
Key : Platform
Value : Win32NT
Name : Platform
Key : PSCompatibleVersions
Value : {1.0, 2.0, 3.0, 4.0…}
Name : PSCompatibleVersions
Key : PSRemotingProtocolVersion
Value : 2.3
Name : PSRemotingProtocolVersion
Key : SerializationVersion
Value : 1.1.0.1
Name : SerializationVersion
Key : WSManStackVersion
Value : 3.0
Name : WSManStackVersion
Visuals
No response
Thanks @gerryleys for opening the issue and providing detailed steps-- we really appreciate it...looks like you have a good fix, would you be interested in contributing a PR? We are hoping to get out a patch release
@SydneyhSmith I created a PR. A small adaptation from the first suggested quick fix. I used the linked PS SecretStore secret with a 'SecureString' datatype object to be treated as a (PAT) token, this makes more sense. This translates later in the code to a PSCredential object with username 'token', and will be handled with 'Basic Authorization'.
@SydneyhSmith Any news on this PR? Is there something I can do or modify? Thanks in advance.
@gerryleys apologies for the delay, we have a few folks out on Spring Break and the rest at PowerShell Summit this week... I'll try to get a review on it in the next week