PowerShell / PowerShellGetv2

PowerShellGet is the Package Manager for PowerShell
https://www.PowerShellGallery.com
MIT License
431 stars 138 forks source link

PowerShellGet 2.1.4: Credential Providers | Authentication is STILL BROKEN #619

Open Jaykul opened 5 years ago

Jaykul commented 5 years ago

I don't know why PowerShell/PowerShellGetv2#133 was closed when this is still so COMPLETELY BROKEN

We cannot work with repositories that require credentials unless we ... ... pass credentials ... with every, ... single ... call.

That's not how any of this is supposed to work.

For example, following the docs for the Azure Artifacts CredProvider, and the blog post from earlier this month we should be able to install the credential provider:

iwr https://raw.githubusercontent.com/microsoft/artifacts-credprovider/master/helpers/installcredprovider.ps1 -out ~\Downloads\installcredprovider.ps1
~\Downloads\installcredprovider.ps1 -AddNetfx

Set something like this:

$ENV:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS = @'
{
   "endpointCredentials": [{
      "endpoint":"https://poshcode.pkgs.visualstudio.com/_packaging/PowerShell/nuget/v2",
      "username":"Jaykul",
      "password":"thisshouldbeareaallylongpattokenstringyougetfromtheweb"
   }]}
'@

Register the feed:

Register-PSRepository -Name Test -Source https://poshcode.pkgs.visualstudio.com/_packaging/PowerShell/nuget/v2

And get a list of modules:

Find-Module * -Repository Test

But what actually happens is that with no error I get no results.

But if I try it with nuget:

nuget sources add -Name Test -Source https://poshcode.pkgs.visualstudio.com/_packaging/PowerShell/nuget/v2

nuget list -source Test

It works fine, and I get a list of modules...

YOU'RE DOING IT WRONG

The problem is that PowerShellGet isn't looking up the credentials against the feed address.

Instead, it's looking up (or rather, relying on nuget to) the credentials against the final url with all the parameters in it. So we can make the Find-Module command work if we change the stored endpoint to this:

$ENV:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS = @'
{
   "endpointCredentials": [{
      "endpoint":"https://poshcode.pkgs.visualstudio.com/_packaging/PowerShell/nuget/v2/FindPackagesById()?id='*'&$skip={0}&$top={1}",
      "username":"Jaykul",
      "password":"thisshouldbeareaallylongpattokenstringyougetfromtheweb"
   }]}
'@

And then re-run:

Find-Module * -Repository Test

But this is because I've hard-coded the token to a particular search.

Of course the result is that when we don't use the environment variable (i.e. Remove-Item ENV:VSS_NUGET_EXTERNAL_FEED_ENDPOINTS) and do a Find-Module we get a "devicelogin" from the credential provider, but the credentials are cached for that specific URL, so if we Find-Module for any other specific module, or try anything else (like Install-Module), they will fail to find the cached credentials, and require going through the "devicelogin" again...

The worst part is that each time we go through the device login, you're creating a new PAT token that you'll never re-use...

Please, fix it again.

twinter-amosfivesix commented 2 years ago

I ran into the same problem as many here, where you are constantly taken to the device flow even thought it seems like you should not be. But using the troubleshooting tips for the Azure Artifacts Credential Provider I figured out that our Azure DevOps authentication setup required multifactor authentication. Maybe that's why I was getting the device flow so much? But even if that's not it, I found a reasonable alternative that means I don't need to enter credentials all the time.

The key is to setup the VSS_NUGET_EXTERNAL_FEED_ENDPOINTS environment variable as talked about here. You follow mostly the steps in Use an Azure Artifacts feed as a private PowerShell repository but instead of passing credentials, you rely on VSS_NUGET_EXTERNAL_FEED_ENDPOINTS.

So basically this:

  1. Create a PAT that has package permissions
  2. Set a user-level environment variable VSS_NUGET_EXTERNAL_FEED_ENDPOINTS to something like: '{"endpointCredentials": [{"endpoint":"https://pkgs.dev.azure.com/<organization>/_packaging/<feed>/nuget/v2", "username":"<youremail>, "password":"<pat>"}]}'
  3. Register-PSRepository -Name "YourFeedName" -SourceLocation "https://pkgs.dev.azure.com/<organization>/_packaging/<feed>/nuget/v2" -PublishLocation "https://pkgs.dev.azure.com/<organization>/_packaging/<feed>/nuget/v2" -InstallationPolicy Trusted
  4. Register-PackageSource -Name "YourFeedName" -Location "https://pkgs.dev.azure.com/<organization>/_packaging/<feed>/nuget/v2" -ProviderName NuGet -Trusted -SkipValidate
    1. You do need this for Install-Module to work.
  5. Make sure it worked with Find-Module -Repository YourFeedName

Setting up the VSS_NUGET_EXTERNAL_FEED_ENDPOINTS environment variable may make you uneasy. But for me it's like using a PAT in a nuget.config like I've done in the past. But you do have to remember to re-generate the PAT and update the environment variable when it expires.

alerickson commented 2 years ago

@twinter-amosfivesix can you try using our latest prerelease version (can be found here). There is an integration with the SecretMangement module that allows for a smoother experience with handling credentials and credential persistence.

CodyBatt commented 2 years ago

@twinter-amosfivesix that's a pretty hefty amount of prerequisites. I was just trying to come up with some simple instructions for people to install a utility module from our Azure Artifacts. For CI/CD that's fine, but just to get a module that's a lot of hoops.

@alerickson I tried the latest prerelease and couldn't get it working but maybe the steps are different since all the commands changed in psg 3.0. IDK... I'm giving up on this and telling people to store their pat in a file and pass -Credential everywhere. Nothing else works.

anamnavi commented 1 year ago

@CodyBatt Yes credential management is different in PowerShellGetV3. It relies on Microsoft.PowerShell.SecretsManagement module, and you can look at this blogpost to get familiar with SecretsManagement, install that module and set up a vault. In order for credential persistence to work with PSGet, it needs SecretsManagement, otherwise if it's not there it will automatically give an error. After the vault is set up with SecretsManagement then all you need to do is create a PSCredentialInfo object like this example here (which assumes you have a vault named "testvault" already).

You should register (or set) the repository with the -CredentialInfo, like this example here

We'll put out a blogpost by end of this month to show all the steps for working with Credentials in PSGetV3, thank you for sharing this feedback!

CodyBatt commented 1 year ago

@anamnavi Right, I tried to set it up with SecretsManagment and didn't get anywhere. Succinct documentation for getting it set up will be a big help.

itsme112358 commented 1 year ago

@twinter-amosfivesix In case you rely on the command during a pipeline, you don't even need to do it manually. It suffices to add a NuGet Authenticate step which creates the environment variable for you. That way you don't need to store a PAT somewhere. Instead it will be generated with the appropriate permissions that the agent actually has.

twinter-amosfivesix commented 1 year ago

Sorry I never got any notifications about replies. I was just trying this again recently and I was not getting the repeated prompt for credentials. Still using PSGetV2. So not sure what was going on there.

And yes NuGet Authenticate is what you'd use in an Azure DevOps pipeline. I haven't gotten there yet.

anamnavi commented 1 year ago

@twinter-amosfivesix are you able to try using PowerShellGetV3? If so, please see my earlier message about using credential management with PSGetV3, thanks!

savagemonitor commented 1 year ago

I did a deep dive today to come up with a cleaner process for using Azure Artifact Feeds that I could hand off to non-technical people. During that dive I found that the "easiest" work-around may just be to have Visual Studio installed. There's more detail below if anyone is interested.

The cleaner process for downloading from feeds using PowerShellGet v2 that I came up with today follows.

One time do this:

install-module powershellget -force -allowclobber

iex "& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) } -AddNetfx"

That gets you the latest version of PowerShellGet and installs the Azure Artifacts Credential Provider. You will need to restart your PowerShell window after you've done the above steps.

Then in PowerShell you can do this:

$repositoryName = <your repository name>
$packageLocation = <your artifacts feed v2 uri>
$vssCredentialProvider = "$env:UserProfile\.nuget\plugins\netfx\CredentialProvider.Microsoft\CredentialProvider.Microsoft.exe"
$tokenJson = ConvertFrom-Json (& $vssCredentialProvider -U $packageLocation -F Json)

$pscred = new-object PSCredential -ArgumentList $tokenJson.Username, (ConvertTo-SecureString $tokenJson.Password -AsPlainText -Force)

Register-PsRepository $repositoryName -SourceLocation $packageLocation -PublishLocation $packageLocation -InstallationPolicy Trusted -Credential $pscred

$moduleName = <name of your module>

install-module $moduleName -SkipPublisherCheck -Credential $pscred

import-module $moduleName -Force

The VssCredentialProvider variable can have "netfx" replaced with "netcore" if you're running in PowerShell Core.

You can get around the credentials request by installing Visual Studio due to a bug where PowerShellGet presumes that $env:NUGET_PLUGIN_PATHS will be set to the location of the credential provider. This always fails on Windows since that environment variable is the root directory where Nuget gets its plugins not a direct pointer to the credential provider. The module really needs to go looking for the credential provider. Linux and MacOS work though since that case falls through to a .NET Core specific scenario and builds out the entire path to the credential provider.

Windows thus always fails the first check for the credential provider and falls back to "vswhere" to locate the provider. If you don't have Visual Studio installed then the check fails.

However, even if you solve the problem by pointing $env:NUGET_PLUGIN_PATHS to the credential provider you've only won half the battle. Your registration will work and even Find-Package will work but install-module and install-package will fail because they cannot authenticate to the feed. I've tried install-package -verbose -debug to narrow down what exactly is going wrong but without something to debug into the output isn't useful. I cannot even locate the source that is raising the issue.

I hope this helps anyone else having the same issue.

CodyBatt commented 1 year ago

We'll put out a blogpost by end of this month to show all the steps for working with Credentials in PSGetV3, thank you for sharing this feedback!

@anamnavi did the blog post get created? Can you link it here?

deadlydog commented 7 months ago

What is the status of this? It seems that Install-PSResource and Find-PSResource still do not work properly with Azure Artifacts and the Azure Artifacts Credential Provider in Microsoft.PowerShell.PSResourceGet v1.0.3; they result in a 401 (Not Authorized) error.

The Install-Module and Find-Module commands work properly though when registered with the same repository URI, so I know that the Azure Artifacts Credential Provider is setup properly.

What's worse though is it seems that the latest version 3.0.23 of PowerShellGet has broken the Install-Module and Find-Module commands completely; see https://github.com/PowerShell/PowerShellGet/issues/58. Luckily I still have PowerShellGet v3.0.12 installed so the commands still work on my local machine, but new servers that I'm setting up are not able to connect to our Azure Artifacts feed without the -Credential parameter.

I created the AzureArtifactsPowerShellModuleHelper module a while back to help solve this issue, but found that sometimes even with that module PowerShellGet 2.2.5 still couldn't access the feed and we'd need to use the prerelease v3 version. With v3.0.23 broken though, and older v3 versions delisted and thus can't be installed, there's not much we can do when v2.2.5 has issues.

Here's a screenshot showing the issue. If you need any other information for troubleshooting let me know; I'm happy to provide anything that can help get this issue resolved.

image

deadlydog commented 7 months ago

I noticed afterward that this issue is for PowerShellGet v2. I've opened this new issue in the PSResourceGet repo: https://github.com/PowerShell/PSResourceGet/issues/1601