KeyInteractive.Common.Api.Users
2.8.1
dotnet add package KeyInteractive.Common.Api.Users --version 2.8.1
NuGet\Install-Package KeyInteractive.Common.Api.Users -Version 2.8.1
<PackageReference Include="KeyInteractive.Common.Api.Users" Version="2.8.1" />
<PackageVersion Include="KeyInteractive.Common.Api.Users" Version="2.8.1" />
<PackageReference Include="KeyInteractive.Common.Api.Users" />
paket add KeyInteractive.Common.Api.Users --version 2.8.1
#r "nuget: KeyInteractive.Common.Api.Users, 2.8.1"
#:package KeyInteractive.Common.Api.Users@2.8.1
#addin nuget:?package=KeyInteractive.Common.Api.Users&version=2.8.1
#tool nuget:?package=KeyInteractive.Common.Api.Users&version=2.8.1
Key Interactive.Common.Api.Users
KeyInteractive.Common.Api.Users is the official .NET client for the Key Interactive Users API
("Key Interactive Accounts"). It gives your app a ready-to-use IApiConsumer/ApiConsumer service
wrapping registration, login, enrollment, profile/password management, and the "Sign in with Key
Interactive" flow (Authorize/Consent/Poll) behind simple method calls, instead of writing HTTP
clients by hand.
This package supersedes the older KeyInteractive.API.Common package (namespace
KeyInteractiveCommon) — if you're migrating from it, see the notes at the bottom of this README.
It's built on KeyInteractive.Common.Api,
which provides the generic API-consuming plumbing (response envelope, retry, JWT handling) that
ApiConsumer inherits.
Requirements
Targets .NET 8.0 or later (net8.0, net10.0).
Installation
dotnet add package KeyInteractive.Common.Api.Users
Configuration
Configure your credentials once at startup with ApiSettings.Configure:
using KeyInteractive.Common.Api.Users.Configuration;
using KeyInteractive.Common.Api.Users.Enums;
ApiSettings.Configure(builder => builder
.ConfigureApiKey("your-api-key")
.ConfigureApplicationId("your-application-id")
.SetApiVersion(EApiVersion.V1)
.SetCustomApiBaseUri("https://accounts.keyinteractive.it/")
.ConfigureDeviceInfo(Environment.MachineName, "127.0.0.1")
.Build());
Dependency injection (Blazor Server / ASP.NET Core)
// Program.cs
builder.Services.AddScoped<ITokenProvider, TokenProvider>();
builder.Services.AddHttpClient<IApiConsumer, ApiConsumer>();
ITokenProvider must be registered Scoped: it holds the current user's JWT for one Blazor
circuit. If your site also talks to another API built on KeyInteractive.Common.Api, give that
consumer its own ITokenProvider and a distinct StorageKeyPrefix — see the
KeyInteractive.Common.Api README for
details. This package's own storage key prefix is fixed to "ki" for backward compatibility with
existing sessions.
Usage examples
Register and log in
using KeyInteractive.Common.Api.Users.DTO.UsersApi;
var registration = await apiConsumer.RegisterBasicUser(new BasicUserRegistrationDto
{
Username = "jdoe",
Email = "jdoe@example.com",
Password = "Sup3rSecret!",
ConfirmPassword = "Sup3rSecret!",
DateOfBirth = new DateTime(1990, 1, 1),
AcceptedPrivacy = true,
AcceptedPrivacyDate = DateTime.UtcNow,
});
var login = await apiConsumer.Login(new UserLoginDTO(
loginCredentials: "jdoe@example.com",
password: "Sup3rSecret!",
deviceName: Environment.MachineName,
deviceIp: "127.0.0.1"));
if (login.Success)
{
Console.WriteLine($"Welcome, {login.LoginResponse.ActiveUser.Name}!");
}
Read the current user's profile
var profile = await apiConsumer.GetProfile();
if (profile.Success)
{
Console.WriteLine(profile.UserProfile.Email);
}
"Sign in with Key Interactive" — server-to-server token exchange
Use a separate ApiConsumer instance configured with your third-party application's own
ApiKey/ApplicationKey (not the Key Interactive site's own):
var exchange = await apiConsumer.ExchangeAuthorizationCode(new AuthorizeTokenExchangeDto
{
Code = codeFromRedirect,
RedirectUri = "https://your-app.example.com/callback",
});
if (exchange.Success)
{
// apiConsumer is now authenticated as the linked user (SetAuthTokens was applied automatically)
var user = exchange.TokenData.ActiveUser;
}
For the "standard"/polling mode (no redirect_uri), poll instead:
var poll = await apiConsumer.PollAuthorizeToken(new AuthorizePollDto { State = yourGeneratedState });
if (poll.Success)
{
// token acquired, same as ExchangeAuthorizationCode
}
else if (poll.ErrorMessage == "authorization_pending")
{
// keep polling
}
Report and read purchases
Report a billable event server-to-server (application credentials only, no user token):
await apiConsumer.ReportPurchase(new ReportPurchaseDto
{
UserId = 42,
ExternalId = "INV-2026-00042", // distinct per renewal for subscriptions
Description = "Pro plan — monthly",
AmountMinor = 1999,
Currency = "EUR",
Status = EPurchaseStatus.Paid,
PurchaseDate = DateTime.UtcNow,
});
ReportPurchase is an idempotent upsert keyed on (your application, ExternalId): resend the same
ExternalId with Status = EPurchaseStatus.Refunded to record a refund.
The authenticated user reads their own purchase history (optionally filtered by date range):
var purchases = await apiConsumer.GetMyPurchases(fromUtc: DateTime.UtcNow.AddMonths(-12));
if (purchases.Success)
{
foreach (var p in purchases.Purchases)
Console.WriteLine($"{p.ApplicationName}: {p.Description} — {p.AmountMinor / 100m} {p.Currency}");
}
Partner assignments and tenant team memberships
Server-to-server: assign a user as "in management to" a partner tenant (reseller model — e.g. a software house reselling your app), and look up which tenant teams a user belongs to:
await apiConsumer.AssignPartner(new AssignPartnerDto { UserId = 42, PartnerCode = "ABCD1234EFGH5678" });
// await apiConsumer.UnassignPartner(new UnassignPartnerDto { UserId = 42, PartnerCode = "ABCD1234EFGH5678" });
var access = await apiConsumer.CheckServiceAccess("ABCD1234EFGH5678");
if (access.ServiceAccess.HasActiveSubscription)
{
// auto-provision access for tier access.ServiceAccess.TierCode
}
// Una software house invita un proprio cliente (magari senza ancora un account KI) a
// diventare cliente sulla vostra piattaforma, per conto del tenant che rappresenta:
var invite = await apiConsumer.InviteClient(new InviteClientDto
{
PartnerCode = tenantPartnerCode,
Email = "cliente@azienda-cliente.it",
});
// invite.Success == false con 400 se il tenant non ha un abbonamento che consente clienti
// Sulla vostra pagina di accettazione, prima che il visitatore sia identificato:
var info = await apiConsumer.GetClientInvitationInfo(token);
// info.InvitationInfo?.CompanyName / ApplicationName / Email (mascherata) / Valid
// Dopo che l'utente ha fatto login/registrazione sul vostro flusso:
var accepted = await apiConsumer.AcceptClientInvitation(new AcceptClientInvitationDto { Token = token });
// accepted.Success crea subito la PartnerAssignment - l'utente non deve essere già enrolled
var memberships = await apiConsumer.GetUserMemberships(new GetUserMembershipsDto { UserId = 42 });
if (memberships.Success)
{
foreach (var m in memberships.Memberships)
Console.WriteLine($"{m.CompanyName} ({m.Role}) — PartnerCode {m.PartnerCode}");
}
// Scoped alla vostra applicazione (a differenza di GetUserMemberships, cross-app): di quali
// VOSTRI partner l'utente è cliente (PartnerAssignment) — lookup singolo dopo il login,
// alternativa mirata a GetPartnerAssignments (bulk, senza filtro per utente):
var assignment = await apiConsumer.GetAssignmentForUser(new GetPartnerAssignmentForUserDto { UserId = 42 });
if (assignment.Success)
{
foreach (var a in assignment.Assignments)
Console.WriteLine($"PartnerCode {a.PartnerCode} — {a.Status}");
}
// GetPartnerAssignments() è paginato lato server (default page=1, pageSize=200, cap 500):
// senza argomenti si comporta come sempre, per leggere oltre la prima pagina passate page/pageSize:
var page2 = await apiConsumer.GetPartnerAssignments(page: 2, pageSize: 200);
// GetPartnerAssignments restituisce solo le assegnazioni CONFERMATE (cliente già loggato).
// Per gli inviti-cliente ancora pendenti (InviteClient, non ancora accettati né revocati):
var pending = await apiConsumer.GetPendingClientInvitations();
if (pending.Success)
{
foreach (var i in pending.Invitations)
Console.WriteLine($"{i.Email} — PartnerCode {i.PartnerCode}, scade {i.ExpiryDate:d}");
}
The authenticated user manages their own team memberships and accepts invitations:
var mine = await apiConsumer.GetMyMemberships();
await apiConsumer.AcceptTeamInvitation(new AcceptTeamInvitationDto { Token = tokenFromEmailLink });
await apiConsumer.LeaveTenant(new LeaveTenantDto { PartnerCode = "ABCD1234EFGH5678" });
Un dipendente Admin di un'azienda cliente può gestire il team dalla vostra piattaforma senza mai avere le credenziali Tenant di quell'azienda (solo credenziali Applicazione, server-to-server):
var invite = await apiConsumer.InviteTeamMemberAsAdmin(new InviteTeamMemberAsAdminDto
{
PartnerCode = tenantPartnerCode,
ActingUserId = currentAdminUserId,
Email = "collega@azienda.it",
Role = ETenantMemberRole.Member,
});
// invite.Success == false con 403 se currentAdminUserId non è un membro Admin di quel tenant
See IApiConsumer for the full list of available operations (registration, enrollment,
profile/password management, purchases, partner assignments, tenant team memberships, and the
full Authorize/Consent/Poll flow).
Migrating from KeyInteractive.API.Common
- Namespace changed from
KeyInteractiveCommon.*toKeyInteractive.Common.Api.Users.*(DTOs, responses, models, enums) andKeyInteractive.Common.Api.*(the response envelopeKiApiResponse/KiApiResponseData, now shared plumbing). IApiConsumer/ApiConsumerpublic surface and behavior are unchanged: same constructor signature, same endpoints, same retry/token-rotation behavior. Existing logged-in users are unaffected — the refresh token storage key prefix (ki_auth_token/ki_auth_refresh_token) is unchanged.ApiSettings.Configure(...)usage inProgram.csis unchanged.
| 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
- KeyInteractive.Common.Api (>= 2.3.0)
-
net8.0
- KeyInteractive.Common.Api (>= 2.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.