NextIteration.SpectreConsole.Auth.Providers.SoftwareOne
2.4.0
dotnet add package NextIteration.SpectreConsole.Auth.Providers.SoftwareOne --version 2.4.0
NuGet\Install-Package NextIteration.SpectreConsole.Auth.Providers.SoftwareOne -Version 2.4.0
<PackageReference Include="NextIteration.SpectreConsole.Auth.Providers.SoftwareOne" Version="2.4.0" />
<PackageVersion Include="NextIteration.SpectreConsole.Auth.Providers.SoftwareOne" Version="2.4.0" />
<PackageReference Include="NextIteration.SpectreConsole.Auth.Providers.SoftwareOne" />
paket add NextIteration.SpectreConsole.Auth.Providers.SoftwareOne --version 2.4.0
#r "nuget: NextIteration.SpectreConsole.Auth.Providers.SoftwareOne, 2.4.0"
#:package NextIteration.SpectreConsole.Auth.Providers.SoftwareOne@2.4.0
#addin nuget:?package=NextIteration.SpectreConsole.Auth.Providers.SoftwareOne&version=2.4.0
#tool nuget:?package=NextIteration.SpectreConsole.Auth.Providers.SoftwareOne&version=2.4.0
NextIteration.SpectreConsole.Auth.Providers.SoftwareOne
SoftwareOne Marketplace credential provider for NextIteration.SpectreConsole.Auth.
Drops a ready-to-use SoftwareOneCredential, SoftwareOneToken, authentication service, Spectre.Console accounts add prompt collector, and accounts list display formatter into a CLI that already uses the core auth package.
Install
dotnet add package NextIteration.SpectreConsole.Auth.Providers.SoftwareOne
Requires the core package (NextIteration.SpectreConsole.Auth, major-capped range [2.3.0,3.0.0)) — NuGet pulls it in transitively. Core 1.x is not supported: core 2.0.0 added a CancellationToken to the interfaces these providers implement.
Quick start
using NextIteration.SpectreConsole.Auth;
using NextIteration.SpectreConsole.Auth.Providers.SoftwareOne;
services.AddCredentialStore(opts =>
{
opts.CredentialsDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".my-cli", "credentials");
});
services.AddHttpClient(); // IHttpClientFactory is required
services.AddSoftwareOneAuthProvider();
// ... then hook up the accounts branch in your Spectre.Console configurator
// (from the core package):
//
// config.AddAccountsBranch();
From that point on:
my-cli accounts add # prompts for SoftwareOne (and any other registered providers)
my-cli accounts list # shows stored credentials with Actor, Base URL, masked token
my-cli accounts select <id>
my-cli accounts delete <id>
Resolving a token in consumer code:
public sealed class MarketplaceClient(SoftwareOneAuthenticationService auth, IHttpClientFactory http)
{
public async Task<string> GetOrdersAsync()
{
var token = await auth.AuthenticateAsync();
using var client = http.CreateClient();
client.BaseAddress = token.BaseUrl;
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(SoftwareOneToken.TokenType, token.ApiToken);
// ... token.Actor / token.Environment let you branch on which flow to use
return await client.GetStringAsync($"/v1/{token.Actor.ToLowerInvariant()}/orders");
}
}
What gets stored
Each accounts add run validates the supplied token against the Marketplace API and, on exactly one match, serialises a SoftwareOneCredential into the encrypted keystore:
| Field | Source | Notes |
|---|---|---|
ApiToken |
hidden prompt | Long-lived portal-issued token |
BaseUrl |
prompt (validated) | Absolute http(s) URL, default https://api.softwareone.com/ |
Environment |
selection prompt | Production / Staging / Test |
Actor |
selection prompt | Operations / Vendor |
TokenId |
API lookup | SoftwareOne-side identifier for the token |
TokenName |
API lookup | Human-readable token name from the portal |
AccountId |
API lookup | SoftwareOne-side account identifier |
AccountName |
API lookup | Human-readable account name |
AccountType |
API lookup | e.g. Vendor, Client, Reseller, Operations |
The token never leaves the credential store unencrypted. accounts list renders the account as name (type), the masked token fingerprint (first four + last four characters) followed by its friendly token name, plus actor and base URL.
Authentication model
SoftwareOne tokens are long-lived and issued out-of-band via the Marketplace portal — there's no exchange or refresh flow. SoftwareOneAuthenticationService.AuthenticateAsync() is therefore a pass-through: it deserialises the selected credential and projects it into a SoftwareOneToken. IsExpired is hard-coded to false, but tokens can be revoked in the portal — a revoked token surfaces as a 401 on the first API call. Consumers should treat 401 as "prompt for a fresh credential" rather than relying on IsExpired.
Add-time validation
Before storing, the collector performs a live GET {BaseUrl}/v1/accounts/api-tokens?eq(token,'…')&limit=2 authenticated with the user-supplied token. Three outcomes:
- Exactly one match → the five
Token*/Account*metadata fields are populated from the response and the credential is stored. - Zero matches or multiple matches → the command fails; the credential is not stored.
- HTTP error (network, non-2xx) → the command fails; the credential is not stored.
This front-loads the "is this token real?" check — no half-valid credentials sitting in the keystore waiting to 401 later. The validation uses a named HttpClient (SoftwareOneCredentialCollector.HttpClientName) so consumers can pre-configure it:
services.AddHttpClient(SoftwareOneCredentialCollector.HttpClientName, c =>
{
c.Timeout = TimeSpan.FromSeconds(10);
});
AddSoftwareOneAuthProvider() registers that named client itself, with
IHttpClientFactory's default logging suppressed (RemoveAllLoggers()).
That matters because the lookup passes the token in the query string, and
Microsoft.Extensions.Http 8.x logs the full request URI at Information
level — so without the suppression any consumer with an information-level
logger would write the plaintext, long-lived token to its log on every
accounts add. (10.x redacts the query, so this is reachable specifically for
consumers on the net8.0 floor.) Pre-configuring the client as above adds to the
same registration and keeps the suppression; it does not affect logging for any
other HttpClient in your application.
Supported platforms
Whatever the core package supports (currently Windows, macOS, Linux on .NET 8 and .NET 10).
References
- SoftwareOne Marketplace REST API — https://docs.platform.softwareone.com/developer-resources/rest-api Endpoints, request/response shapes, authentication model, and how to generate API tokens from the Marketplace portal.
License
MIT © Stuart Meeks
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Http (>= 10.0.12)
- NextIteration.SpectreConsole.Auth (>= 2.3.0 && < 3.0.0)
- Spectre.Console (>= 0.57.2)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- NextIteration.SpectreConsole.Auth (>= 2.3.0 && < 3.0.0)
- Spectre.Console (>= 0.57.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.4.0 | 238 | 10/13/2026 |
| 2.3.0 | 139 | 10/6/2026 |
| 2.2.0 | 160 | 9/21/2026 |
| 2.1.0 | 143 | 8/31/2026 |
| 2.0.0 | 140 | 8/27/2026 |
| 1.0.1 | 214 | 8/22/2026 |
| 1.0.0 | 126 | 8/21/2026 |
| 0.5.0 | 593 | 7/24/2026 |
| 0.4.0 | 328 | 6/20/2026 |
| 0.3.3 | 368 | 6/10/2026 |
| 0.3.2 | 427 | 5/3/2026 |
| 0.3.1 | 136 | 5/3/2026 |
| 0.3.0 | 224 | 4/18/2026 |
| 0.2.0 | 137 | 4/18/2026 |
| 0.1.1 | 137 | 4/17/2026 |
| 0.1.0 | 145 | 4/17/2026 |