MaksIT.IdentityHub.Client 3.1.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package MaksIT.IdentityHub.Client --version 3.1.2
                    
NuGet\Install-Package MaksIT.IdentityHub.Client -Version 3.1.2
                    
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="MaksIT.IdentityHub.Client" Version="3.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MaksIT.IdentityHub.Client" Version="3.1.2" />
                    
Directory.Packages.props
<PackageReference Include="MaksIT.IdentityHub.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 MaksIT.IdentityHub.Client --version 3.1.2
                    
#r "nuget: MaksIT.IdentityHub.Client, 3.1.2"
                    
#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 MaksIT.IdentityHub.Client@3.1.2
                    
#: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=MaksIT.IdentityHub.Client&version=3.1.2
                    
Install as a Cake Addin
#tool nuget:?package=MaksIT.IdentityHub.Client&version=3.1.2
                    
Install as a Cake Tool

MaksIT.IdentityHub.Client

NuGet License: MIT Line Coverage Branch Coverage Method Coverage

NuGet client for MAKS-IT Identity Hub.

Hub controllers use result.ToActionResult(); this client maps HTTP with Result.FromHttpResponseAsync / Result{T}.FromHttpResponseAsync.

Surfaces

Interface Use when Auth
IIdentityHubIdpClient Covered apps, Google/Microsoft challenge/redeem, mailbox Anonymous + external Hub JWT for mailbox only
IIdentityHubAdminClient Admin login, users, audit, debug/sidecar AdminAccessToken (global admin for most)
IIdentityHubClient Both Combines the two

SignalR /hubs/sidecar-status is WebUI-only (not wrapped).

Token names (do not mix)

Name What it is Where
AdminAccessToken Hub JWT for admin Default Bearer for admin APIs; set by LoginAsync / RefreshTokenAsync
ExternalLoginAccessToken Hub JWT for external from IdP redeem Mailbox Bearer only; set by RedeemLoginExternalAsync
MailboxAccessTokenResponse.IdpAccessToken IdP mailbox access token Response body — not a Hub JWT

IdP challenge/redeem never set AdminAccessToken.

Install

dotnet add package MaksIT.IdentityHub.Client

DI

IdP-only (PostClient mailbox or Google/Microsoft product login — no Hub admin login):

services.AddIdentityHubIdpClient("https://identity.maks-it.com");
// inject IIdentityHubIdpClient

Full API (admin + IdP):

services.AddIdentityHubClient(opts => {
  opts.BaseAddress = "https://identity.maks-it.com";
});
// inject IIdentityHubClient, or IIdentityHubIdpClient / IIdentityHubAdminClient

Examples (IdP only — no Hub admin login)

Register once with AddIdentityHubIdpClient, inject IIdentityHubIdpClient. Generate a PKCE code_verifier / S256 code_challenge in the app. postLogin must be an HTTPS host listed in Hub AllowedPostLoginHosts.

Sign-in with Google or Microsoft

Use when the app only needs a Hub session for an external user (no mailbox / IMAP). Omit mailboxPurpose (default false) so Hub requests only openid email profile.

// 1) Start IdP authorize (follow 302 Location, or open it in a browser / system web view)
var challenge = await idp.GetLoginExternalChallengeAsync(
  provider: "Google", // or "Microsoft"
  postLogin: "https://app.maks-it.com/desktop-callback",
  codeChallenge: codeChallenge);

if (!challenge.IsSuccess)
  throw new InvalidOperationException(string.Join("; ", challenge.Messages));

var authorizeUrl = challenge.Value; // IdP authorize URL

// 2) After IdP redirects to Hub callback, Hub redirects to postLogin?code=<otc>
// 3) Redeem OTC + PKCE verifier → Hub JWT for the external principal
var redeem = await idp.RedeemLoginExternalAsync(otcCode, codeVerifier);
if (!redeem.IsSuccess)
  throw new InvalidOperationException(string.Join("; ", redeem.Messages));

var hubJwt = redeem.Value!.ExternalLoginAccessToken;
// Use hubJwt as Bearer against your own APIs / covered product — not for Hub admin APIs.
// Do not call LoginAsync; AdminAccessToken stays unset.

PostClient — mailbox access (XOAUTH2)

Use when PostClient needs a Google/Microsoft mailbox access token via Hub. Pass mailboxPurpose: true so Hub requests configured mailbox scopes and can vault a refresh token.

// 1) Challenge with purpose=mailbox (consent + offline access for Google)
var challenge = await idp.GetLoginExternalChallengeAsync(
  provider: "Google", // or "Microsoft"
  postLogin: "https://postclient.maks-it.com/desktop-callback",
  codeChallenge: codeChallenge,
  mailboxPurpose: true);

if (!challenge.IsSuccess)
  throw new InvalidOperationException(string.Join("; ", challenge.Messages));

// 2) User completes IdP; Hub OTC arrives on postLogin
var redeem = await idp.RedeemLoginExternalAsync(otcCode, codeVerifier);
if (!redeem.IsSuccess)
  throw new InvalidOperationException(string.Join("; ", redeem.Messages));
// idp.ExternalLoginAccessToken is set from redeem

// 3) Exchange external Hub JWT for IdP mailbox token (IMAP/POP/SMTP XOAUTH2)
var mailbox = await idp.GetMailboxAccessTokenAsync();
if (!mailbox.IsSuccess)
  throw new InvalidOperationException(string.Join("; ", mailbox.Messages));

var idpMailboxToken = mailbox.Value!.IdpAccessToken;
// Use idpMailboxToken with the IdP mail server — never treat it as a Hub JWT.

Later mailbox renewal (Hub stays transparent — IdP refresh is vaulted on Hub):

// If the Hub external session expired, renew it (anonymous IdP surface — not admin refresh)
var session = await idp.RefreshLoginExternalAsync(storedHubRefreshToken);
if (!session.IsSuccess)
  throw new InvalidOperationException(string.Join("; ", session.Messages));
// session.Value.ExternalLoginAccessToken is set on idp as well

var mailbox = await idp.GetMailboxAccessTokenAsync();
// or: GetMailboxAccessTokenAsync(externalLoginAccessToken: session.Value!.ExternalLoginAccessToken);

While the Hub external session is still valid, call GetMailboxAccessTokenAsync alone — Hub refreshes the IdP mailbox token from its vault.

Admin (optional)

Only when the app manages Hub users / audit (WebUI-style). Not needed for PostClient or Google/Microsoft product login.

services.AddIdentityHubClient("https://identity.maks-it.com");

IIdentityHubAdminClient admin = ...;
var login = await admin.LoginAsync(new LoginRequest { Username = "admin", Password = "..." });
var users = await admin.SearchUsersAsync(new SearchUserRequest { PageNumber = 1, PageSize = 25 });
await admin.LogoutAsync(new LogoutRequest());

PowerShell

See MaksIT.IdentityHub.Client.PowerShell. Connect-IdentityHub -BaseAddress <url> without -AdminAccessToken for IdP-only; optional -ExternalLoginAccessToken for mailbox.

E2E (manual)

PowerShell scenarios against a running Hub (Compose from maksit-identity-hub): src/e2e-tests/README.md. Not run in CI.

pwsh -File .\src\e2e-tests\Test-IdentityHubE2E.ps1

Contributing

See CONTRIBUTING.md and homelab works/repostories-maintenance/nuget-trusted-publishing.md.

Product Compatible and additional computed target framework versions.
.NET 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

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
4.0.0 96 11/3/2026
3.1.3 106 10/28/2026
3.1.2 96 10/28/2026
3.1.1 100 10/28/2026
3.0.0 105 10/27/2026
2.2.0 108 9/24/2026
2.1.2 117 9/20/2026
2.1.1 113 9/19/2026
2.1.0 130 9/18/2026
2.0.1 145 9/17/2026
2.0.0 112 9/17/2026
1.6.0 118 9/16/2026
1.5.1 141 8/21/2026
1.5.0 172 8/18/2026
1.4.3 133 8/16/2026
1.4.1 133 8/12/2026
1.4.0 137 8/1/2026
1.3.0 130 7/30/2026
1.1.0 140 7/27/2026