Noodloft.Identity.Client 0.0.1-alpha1

This is a prerelease version of Noodloft.Identity.Client.
dotnet add package Noodloft.Identity.Client --version 0.0.1-alpha1
                    
NuGet\Install-Package Noodloft.Identity.Client -Version 0.0.1-alpha1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Noodloft.Identity.Client" Version="0.0.1-alpha1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Noodloft.Identity.Client" Version="0.0.1-alpha1" />
                    
Directory.Packages.props
<PackageReference Include="Noodloft.Identity.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Noodloft.Identity.Client --version 0.0.1-alpha1
                    
#r "nuget: Noodloft.Identity.Client, 0.0.1-alpha1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Noodloft.Identity.Client@0.0.1-alpha1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Noodloft.Identity.Client&version=0.0.1-alpha1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Noodloft.Identity.Client&version=0.0.1-alpha1&prerelease
                    
Install as a Cake Tool

Noodloft.Identity.Client

Authenticate against a Noodloft Identity server from .NET, and call your own APIs with the resulting token.

Targets net8.0 and net10.0. Its only dependency is Microsoft.Extensions.Http.

Install

dotnet add package Noodloft.Identity.Client

Native login (no browser)

The native flow is a loop: send what you have, and the server tells you what is still missing. This is how you sign a user in from a desktop app, a game, or a CLI without opening a browser. It requires a first-party client.

var client = new NoodloftAuthClient(new NoodloftClientOptions
{
    BaseUrl    = "https://id.example.com",
    TenantSlug = "acme",
    ClientId   = "acme-native",
    RedirectUri = "com.example.app://callback", // registered exactly on this client
    Scope       = "openid profile email offline_access" // only granted scopes
});

// Users sign in with their USERNAME, not their email address.
var step = await client.StartNativeLoginAsync(username, password);

while (step.IsPending)
{
    if (step.NeedsEnrollment)
    {
        // The tenant requires MFA and this user has nothing enrolled yet — enroll inline.
        step = await client.BeginTotpEnrollmentAsync(step);
        ShowQrCode(step.TotpUri);                       // or step.TotpSecretBase32 for manual entry
        step = await client.ConfirmTotpEnrollmentAsync(step, await PromptAsync("code"));
        ShowRecoveryCodesOnce(step.BackupCodes);        // shown once, never retrievable again
    }
    else
    {
        // step.RequiredFactor is one of: otp, email_otp, sms_otp, backup_code, webauthn
        step = await client.SubmitFactorAsync(step, await PromptAsync(step.RequiredFactor));
    }
}

// Signed in. Tokens are already saved to the token store.

Browser login (PKCE)

Third-party clients must use the browser flow. Open the URL, capture the redirect, verify the state, exchange.

var request = client.BeginWebLogin();
OpenInBrowser(request.Url);

var (code, state) = await WaitForRedirectAsync();
if (state != request.State) throw new InvalidOperationException("State mismatch — discard this response.");

await client.CompleteWebLoginAsync(request, code);

For browser step-up, call BeginWebLogin(acrValues: "mfa", maxAgeSeconds: 300).

The current client package does not generate DPoP proofs or configure an mTLS client certificate. If discovery returns require_sender_constrained_tokens: true, use a DPoP-capable OAuth library or a trusted mTLS gateway for token acquisition and API calls.

Calling your API

var api = new NoodloftApiClient(client, new Uri("https://api.example.com"));
var orders = await api.GetAsync<List<Order>>("/orders");

NoodloftApiClient attaches the bearer token, and on a 401 refreshes once and retries. Failures are typed so you can tell them apart:

Exception Meaning
NoodloftAuthException with Error == "not_signed_in" The token was rejected and refreshing did not help. Sign in again.
NoodloftAuthException with Error == "insufficient_user_authentication" Step-up required. Re-authenticate with acrValues: "mfa".
NoodloftAuthException with Error == "insufficient_permission" Authenticated, but lacking the permission. Retrying will not help.
NoodloftApiException Your API returned a non-auth error. Body carries the response.

Dependency injection

builder.Services.AddNoodloftClient(opts =>
{
    opts.BaseUrl    = "https://id.example.com";
    opts.TenantSlug = "acme";
    opts.ClientId   = "acme-spa";
    opts.RedirectUri = "https://app.example.com/callback";
    opts.Scope       = "openid profile email offline_access";
    opts.ApiBaseUrl  = "https://api.example.com"; // enables relative paths on injected NoodloftApiClient
});

Both clients are registered scoped over a pooled named HttpClient. A token store holds one user's session, so a singleton would leak it across requests.

Storing tokens

The bundled InMemoryTokenStore loses the session when the process exits. To keep users signed in, implement ITokenStore over something durable and encrypted — DPAPI or the platform keyring on a desktop, the secure enclave on mobile, an encrypted IDistributedCache entry in a web backend.

The refresh token can mint new access tokens until revoked. Treat it like a password.

Notes

  • Access tokens are opaque. Do not try to decode one — APIs introspect them against the server. A resource-side cache can delay revocation until its TTL unless push eviction is configured. Use GetUserInfoAsync() for identity details.
  • Every endpoint is tenant-scoped under /t/{TenantSlug}/. The SDK derives those URLs for you.
  • Refresh tokens rotate and are single-use. Concurrent calls on one NoodloftAuthClient instance are serialized. If several client instances or processes share one token store, that store must provide cross-instance coordination as well.

Licence

MIT

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Noodloft.Identity.Client:

Package Downloads
Noodloft.Components.Games.Godot

Godot 4 glue for Noodloft components: logging, user:// save and encrypted token storage, and the WebSocket relay MultiplayerPeer used by server-authoritative games. The nodes and resources are NOT in this package. Godot keys C# script types to a res:// path, which a type inside a DLL does not have, so they ship as addon source instead.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.1-alpha1 230 8/1/2026