-
Notifications
You must be signed in to change notification settings - Fork 258
NuGet cross plat authentication plugin
- Status: Implemented
- Author(s): Nikolche Kolev, Alex Mullans
6486 - NuGet Cross Plat Credential Plugin
Currently NuGet has a simple plugin model that's used for authentication against protected feeds. However, it's only available in VS and NuGet.exe.
- Consumers need to be able to restore against protected feeds cross-plat in msbuild/dotnet.
- Additionally, we want to establish trust between the process running restore and the credential provider before running it.
The customers are all users of authenticated feeds like VS Team Services (Package Management), MyGet, etc.
To facilitate the cross-plat authentication NuGet will extend the extensibility plugin model introduced in version 4.3 as described in the NuGet Package Download Plugin spec. The protocol established there is versioned and will be extended to further fit the needs of the authentication plugin.
The new protocol version for the Plugin will be 2.0.0. Each of the new requirements below are required only for version 2.0.0 of plugins. Every requirement/behavior specified by the previous spec still stands for version 2.0.0 plugins, unless called out specifically.
The high level overview how the plugin integration will work in the authentication case:
- NuGet discovers available plugins.
- Due to performance considerations, NuGet will only launch a plugin if/when it has to (401 response from the server).
- NuGet will iterate over the plugins in a priority order where applicable, and launch each one.
- NuGet will ask the plugin for credentials. The plugin should provide valid credentials ONLY if it can help authenticate the user for the feed.
- NuGet client tools will shutdown plugins when they are no longer needed. Read more - Plugin life-cycle
Each Version 2.0.0 plugin must meet the requirements previous specified in the NuGet Package Download in addition to some new ones warranted by the current use-case.
For simplicity, the requirements will be duplicated from the other spec and altered as required.
-
Have a valid, trusted Authenticode signature.On Windows we require that plugins have a valid Authenticode signature. On non-Windows platforms we will have additional requirements defined at a later point.Task - Support stateless launching under the current security context of NuGet client tools. For example, NuGet client tools will not perform elevation or additional initialization outside of the plugin protocol described later.
-
Be noninteractive.Support both interactive and non-interactive scenarios. Some operations are allowed to be interactive, as defined below. - Adhere to the negotiated plugin protocol version.
- Respond to all requests within a reasonable time period.
- Honor cancellation requests for any in-progress operation.
In addition to the Package Download operation, the new version of the plugin will support Authentication. The plugin in this case is only responsible for:
- Providing valid token to NuGet, that Nuget Client Tools will use for all https communication to the given source.
This token will be sent to the server by NuGet Client Tools in the basic auth form, similar to the previous credential provider workflow.
Currently NuGet the 1st iteration of extensibility plugins are discovered via an environment variable NUGET_PLUGIN_PATHS, priority preserved. This behavior will remain backwards compatible. Additionally, a convention based plugin discovery will be added. No ordering is defined for plugins discovered based on the later defined conventions.
Due to the fact that NuGet runs under both .NET framework and .NET Core, there will be small differences in the discovery. The plugins will be discovered as follows:
-
An environment variable NUGET_PLUGIN_PATHS, priority reserved of the plugins reserved. If the NUGET_PLUGIN_PATHS environment variable is set, it overrides the convention based plugin discovery.
The environment variable should contain a full path to the executable, exe in the .NET Framework case and dll in the .NET Core case. It's at the user's discretion to make sure that the plugins are executable under that runtime.
-
User-location - The NuGet Home location - %UserProfile%/.nuget/plugins/
.NET Core based plugins should be installed in:
%UserProfile%/.nuget/plugins/netcore
.NET Framework based plugins should be installed in:
%UserProfile%/.nuget/plugins/netfx
Each plugin will be installed in it's own folder.
The plugin entry point will be the name of the installed folder, with the .dll extensions for .NET Core, and .exe extension for .NET Framework.
Example:
.nuget plugins netfx myFeedCredentialProvider myFeedCredentialProvider.exe nuget.protocol.dll netcore myFeedCredentialProvider myFeedCredentialProvider.dll nuget.protocol.dll
There's a gap in this approach.
-
Predetermined location in Visual Studio and dotnet
In Visual Studio the fixed location is a folder relative to the NuGet assemblies. NuGet itself is installed in
Common7\IDE\CommonExtensions\Microsoft\NuGet
The plugins folder location will beCommon7\IDE\CommonExtensions\Microsoft\NuGet\Plugins
.In the SDK, the fixed location is a folder relative to the NuGet assemblies. NuGet itself is currently installed in
dotnet\SDK\version\
The plugins folder location will bedotnet\SDK\version\Plugins
All plugins should be self-contained, and install all their dependencies in their respective folders.
Different versions of nuget.exe/dotnet.exe could be using the same plugins. The obvious issue arises when an older client tries to execute a newer plugin. If a dotnet.exe under the 2.x runtime, tries to launch a 3.x plugin. A potential approach would involve adding the target framework under which the plugin has been built. As this a corner-case scenario that's easily resolvable, the current approach is that we go with the cleaner approach of not including any compatibility checks.
On cross-plat, we will rely on the fact that plugins can be executed under the dotnet runtime. The dll discovered will be run with:
dotnet exec plugin.dll
Unlike Package Download, Authentication should be a package agnostic operation. NuGet will reuse the same operation claims message as before, but query without any parameters.
For simplicity the following rules should be followed by the plugins. On the request for source specific operation, the plugin should not return operations such as Authentication. On the request for source agnostic operations, the plugin should not return operations such as PackageDownload. It's the consumers' job to make sure it doesn't attempt to delegate an unsupported operation to a plugin.
All the protocol rules defined by Version 1.0.0 stand for Version 2.0.0.
The version negotiation stays the same as defined in Version 1.0.0.
The following message will be amended for version 2.0.0 of the plugin.
- Get Operation Claims
-
Request direction: NuGet -> plugin
- The request will contain:
- the service index.json for a package source
- the package source repository location
- A response will contain:
- a response code indicating the outcome of the operation
- an enumerable of supported operations if the operation was successful. If a plugin does not support the package source, the plugin must return an empty set of supported operations.
If the service index and package source are null, then the plugin can answer with authentication.
The following additional messages are required to support the authentication operation.
- The request will contain:
- Authentication Credentials
- Request direction: NuGet -> plugin
- The request will contain:
- Uri
- isRetry
- NonInteractive
- CanShowDialog
- A response will contain
- Username
- Password
- Message
- List of Auth Types
- MessageResponseCode
When the client calls the plugin with a Get Authentication Credentials, the plugins need to conform to the interactivity switch and respect the dialog switch.
The following table summarizes how the plugin should behave for all combinations.
IsNonInteractive | CanShowDialog | Plugin behavior |
---|---|---|
true | true | The IsNonInteractive switch takes precedence over the dialog switch. The plugin is not allowed to pop a dialog. This combination is only valid for .NET Framework plugins |
true | false | The IsNonInteractive switch takes precedence over the dialog switch. The plugin is not allowed to block. This combination is only valid for .NET Core plugins |
false | true | The plugin should show a dialog. This combination is only valid for .NET Framework plugins |
false | false | The plugin should/can not a dialog. The plugin should use device flow to authenticate by logging an instruction message via the logger. This combination is only valid for .NET Core plugins |
In the previous credential plugin implementation, the experience in Visual Studio and NuGet.exe was different. NuGet.exe used the plugin, while in Visual Studio, authentication was done by relying on an extra VSIX that brings in a Credential provider API.
For performance considerations we will keep the same approach for Visual Studio. As a fallback, NuGet will use the credential plugins, if no other Credential Providers in Visual Studio can satisfy the scenario.
In the old plugin model, NuGet.exe discovered the plugins next to it's executable location. Now NuGet.exe will be able to depend on the plugins the same way dotnet.exe, msbuild.exe and Visual Studio do. That means, that the version of MsBuild that NuGet.exe uses (latest by default unless specifically specified) will dictate which built in plugins NuGet.exe discovers.
MSBuild.exe and dotnet.exe do not prompt by default like Visual Studio and NuGet.exe do. Because of that, the authentication strategy for dotnet.exe and MsBuild.exe will be device flow. The default behavior is that no authentication will happen in the plugin unless an interactivity flag is being passed to the plugin.
Whenever NuGet cannot authenticate, a clear error message suggesting that they might need to pass an extra flag will be displayed. When the flag is passed, the build/restore will block and the user will be provided with instructions on how to complete the authentication. Once that's completed, the action will resume.
Currently there are 2 supported plugin operations. To avoid the cost of launching a plugin, NuGet will cache known operation claims.
For example, plugin CredentialProvider handles authentication, but does not support PackageDownload for feed1 or feed2.
The cache is located in
The plugins currently control different operations in NuGet. Because all of these are independent of each other, it is very hard to understand whether a plugin is still needed. We additionally want to avoid starting a new process too often, so because of that, NuGet will manage the plugin lifetime with idleness. In practice, idleness is only relevant in Visual Studio. In the command-line scenarios, the plugin will die when the NuGet process ends.
Currently the plugins need to be authenticode on signed on Windows and Mono platforms. There is no such requirement for Linux/Mac systems yet. Follow up issue.
Check out the proposals in the accepted
& proposed
folders on the repository, and active PRs for proposals being discussed today.