LaVie.HealthPass.SDK 1.0.43

dotnet add package LaVie.HealthPass.SDK --version 1.0.43
                    
NuGet\Install-Package LaVie.HealthPass.SDK -Version 1.0.43
                    
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="LaVie.HealthPass.SDK" Version="1.0.43" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LaVie.HealthPass.SDK" Version="1.0.43" />
                    
Directory.Packages.props
<PackageReference Include="LaVie.HealthPass.SDK" />
                    
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 LaVie.HealthPass.SDK --version 1.0.43
                    
#r "nuget: LaVie.HealthPass.SDK, 1.0.43"
                    
#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 LaVie.HealthPass.SDK@1.0.43
                    
#: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=LaVie.HealthPass.SDK&version=1.0.43
                    
Install as a Cake Addin
#tool nuget:?package=LaVie.HealthPass.SDK&version=1.0.43
                    
Install as a Cake Tool

LaVie HealthPass SDK

The HealthPass SDK is a .NET client for the HealthPass identity system. It gives a typed, thread-safe client for managing users, groups, roles, permissions and external mappings, and for signing users in headlessly with PKCE or Google OAuth.

Features

  • Headless Authentication: PKCE-based credential login, encrypted group-gated login, and Google OAuth sign-in URLs.
  • Token Refresh & Revocation: exchange an auth code for an access/refresh token pair, refresh it (the refresh token rotates) and revoke it.
  • Secure API Integration: every request is signed with your AppKey/AppSecret and its body is encrypted with the HealthPass public key. The SDK does both automatically.
  • Entity Management: CRUD operations for Users, Roles, Groups, and Permissions.
  • External Mappings: link HealthPass users to identifiers from your own system.
  • Bulk Associations: assign or remove roles, groups and permissions in one call.
  • Multi-Targeting: netstandard2.0 and net10.0. Projects targeting .NET 6 to .NET 9 use the netstandard2.0 build; only .NET 10 uses the net10.0 build.

1. Installation & Setup

Install Package

dotnet add package LaVie.HealthPass.SDK

Supported runtimes: modern .NET (.NET 5 and later). .NET Framework is not supported, because request encryption needs RSA-OAEP with SHA-256, which the default RSA provider of .NET Framework does not offer.

Configuration

Add the HealthPass settings to your appsettings.json:

{
  "HealthPass": {
    "BaseUrl": "https://auth.healthpass.com",
    "AppKey": "your_app_key",
    "AppSecret": "your_app_secret",
    "RsaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\n...provided by HealthPass...\n-----END PUBLIC KEY-----"
  }
}
Setting Description
BaseUrl Base URL of the HealthPass API. Must be an absolute http or https URL.
AppKey / AppSecret Credentials of your application in HealthPass.
RsaPublicKeyPem The HealthPass public key (PEM), provided to you by HealthPass. Both BEGIN PUBLIC KEY and BEGIN RSA PUBLIC KEY PEM formats are accepted.

All four settings are required. Registration (AddHealthPassSdk) throws an ArgumentException at startup if one is missing, or if BaseUrl is not an absolute http/https URL, or if RsaPublicKeyPem is not a usable RSA public key (a truncated or malformed key is refused at startup, not on the first encrypted call).

RsaPublicKeyPem is a public key, not a secret. It is bound from configuration rather than compiled into the SDK so it can be rotated, or differ per environment, without upgrading the package.

Important: SDK Access Activation. Your application must have the "Allow SDK Access" toggle enabled in the HealthPass Admin Dashboard. If it is disabled, all SDK requests fail with 401 Unauthorized.

Important: keep your server clock accurate. Each request is time-stamped, and the API answers 401 to a request whose timestamp is too far from its own clock. Keep your servers synchronised with a time service (NTP).

Service Registration

Register the SDK in your Program.cs or Startup.cs:

using LaVie.HealthPass.SDK.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Binds BaseUrl, AppKey, AppSecret and RsaPublicKeyPem from the "HealthPass" section
builder.Services.AddHealthPassSdk(builder.Configuration.GetSection("HealthPass"));

Two other overloads exist: AddHealthPassSdk(HealthPassSdkOptions options) and AddHealthPassSdk(baseUrl, appKey, appSecret, rsaPublicKeyPem). Although rsaPublicKeyPem of the latter has a default value, omitting it throws, because the key is required.

Getting the client

Inject IHealthPassClient. It exposes one sub-client per area: Auth, Users, Groups, Roles, Permissions and Mappings.

using LaVie.HealthPass.SDK;

public class LoginService
{
    private readonly IHealthPassClient _client;
    public LoginService(IHealthPassClient client) => _client = client;
}

The examples below use client for an injected IHealthPassClient.


2. Authentication Flow (PKCE)

The SDK uses a Proof Key for Code Exchange (PKCE) flow for headless login: LoginAsync returns a short-lived AuthCode, and ExchangeTokenAsync trades it, together with your original verifier, for tokens.

Step 1: Initialize PKCE

Use PkceHelper to generate the verifier and challenge. Keep the verifier on your side until Step 3.

using LaVie.HealthPass.SDK.Models;

var verifier = PkceHelper.GenerateCodeVerifier();
var challenge = PkceHelper.ComputeChallenge(verifier);

Step 2: Login Handshake

Perform a credential-based login to receive an AuthCode.

var loginResult = await client.Auth.LoginAsync(new SdkLoginRequest
{
    EmailOrUserName = "user@example.com",
    Password = "Password123!",
    CodeChallenge = challenge,          // required
    IpAddress = "203.0.113.7"           // optional: the end user's IP, recorded in the login audit
});

SdkLoginResult contains AuthCode, caller, cid, UserName, Email, FirstName, LastName, PhoneNumber, Groups, Roles and Permissions. Profile fields honour your application's enabled claims and masking rules, so a disabled claim comes back empty and a masked one comes back masked.

LoginAsync fails with a HealthPassSdkException (HTTP 400, see §7) when:

  • CodeChallenge is missing (CodeChallengeRequired);
  • MFA is actually required to complete this login — the application has MFA on and the user has TOTP/passkey enabled for it, or a per-user MFA override applies (MfaNotSupportedInSdk; use the encrypted login below for those accounts). An account with TOTP or a passkey enabled is fine to sign in through the SDK as long as the application doesn't require MFA for it — the account setting alone does not trigger this;
  • the application requires Terms & Conditions the user has not accepted (Code == "TermsAcceptanceRequired") — the user must accept them once in the HealthPass web portal or mobile app. The SDK cannot accept terms on a user's behalf: consent has to come from a person actually reading the text, not a server-to-server call.

Without IpAddress, the audit records the address the API sees your server connecting from.

Step 3: Token Exchange

Exchange the AuthCode and the original verifier for tokens.

var tokenResult = await client.Auth.ExchangeTokenAsync(new SdkTokenExchangeRequest
{
    AuthCode = loginResult.AuthCode,
    CodeVerifier = verifier,
    ExternalUserId = "EMR-USR-44219"    // optional: persisted as this user's external mapping for your application
});

string jwt = tokenResult.AccessToken;
string refreshToken = tokenResult.RefreshToken;
DateTime refreshExpires = tokenResult.RefreshTokenExpires;

Step 4: Refresh

Use the refresh token to obtain a new access token without prompting the user. The refresh token rotates: store the new RefreshToken from the result and discard the old one.

var refreshedResult = await client.Auth.RefreshAsync(refreshToken);
string newJwt = refreshedResult.AccessToken;
refreshToken = refreshedResult.RefreshToken;

Step 5: Token Revocation

Invalidate a refresh token to end a session immediately.

await client.Auth.RevokeTokenAsync(refreshToken);

3. Specialized Authentication

Encrypted Group-Gated Login

Restrict authentication to members of a specific group using the encrypted login envelope. If the user is not in the group, the API answers 403 Forbidden.

Each credential field is encrypted with your RsaPublicKeyPem through SdkBodyEncryptor.Encrypt (the SDK then encrypts the whole request body again):

using LaVie.HealthPass.SDK.Internal;
using LaVie.HealthPass.SDK.Models;

string rsaPublicKey = configuration["HealthPass:RsaPublicKeyPem"];

var result = await client.Auth.LoginEncryptedAsync(new SdkEncryptedRequest
{
    EncryptedEmailOrUserName = SdkBodyEncryptor.Encrypt("user@example.com", rsaPublicKey),
    EncryptedPassword = SdkBodyEncryptor.Encrypt("Password123!", rsaPublicKey),
    EncryptedGroupCode = SdkBodyEncryptor.Encrypt("ADMIN_PORTAL_ACCESS", rsaPublicKey),
    CodeChallenge = challenge,
    CodeChallengeMethod = "S256"
});

The result has the same shape as for LoginAsync, and CodeChallenge is optional here (without one, no AuthCode is returned). Exchange result.AuthCode with ExchangeTokenAsync as in Step 3.

EncryptedGroupCode is optional: leave it unset (or null) for a plain encrypted login with no group restriction.

Two differences from LoginAsync: this channel does not prompt for, or reject, HealthPass MFA (an MFA-enabled account can sign in), and a missing group membership is a 403. Like LoginAsync, it fails with HTTP 400 when the user has not accepted the application's Terms & Conditions.

Google OAuth

Google sign-in is a browser round trip:

  1. Ask the SDK for the sign-in URL and redirect the user's browser to it.
var google = await client.Auth.GetGoogleLoginUrlAsync(
    callbackUrl: "https://myapp.com/callback",
    codeChallenge: challenge,
    requiredGroupCode: "ADMIN",   // optional: only members of this group can sign in
    platform: "web");             // optional: "web" or "mobile", echoed back to your callback

// Redirect the user's browser to: google.LoginUrl
  1. After the user signs in with Google, the API redirects the browser to callbackUrl with these query parameters: code (the auth code), caller, and platform (when you sent one). Any query string already on callbackUrl is kept.
  2. Exchange code with ExchangeTokenAsync, using the verifier that matches the codeChallenge you sent.

Notes:

  • The origin of callbackUrl must be registered for your application in HealthPass, otherwise the API refuses to redirect.
  • If the user has not accepted the application's Terms & Conditions, the redirect carries error=terms_acceptance_required instead of code.
  • Google sign-in through the SDK does not prompt for the user's HealthPass MFA.
  • codeChallengeMethod defaults to "S256".

4. Users Management (client.Users)

Every method below that takes a userRef argument accepts either the long caller identifier (~346 chars) or the short cid identifier (~22 chars). Login results, CreateAsync and the lookup methods return both. The server resolves both formats transparently, so you never need to tell it which one you are passing. The cid is the shorter of the two and is the better one to store.

Method Description
CreateAsync(dto) Creates a new user and returns a UserDto (includes both caller and cid). Requires Allow User Registration to be enabled on your application, otherwise the API answers 403. The new user is assigned to your application, but sign-in only succeeds right away if the application has no pending Terms & Conditions for them — otherwise LoginAsync/LoginEncryptedAsync answer TermsAcceptanceRequired (see §2, §7) until the user accepts them once in the HealthPass web portal or mobile app.
GetAsync(userRef) Fetches a user; null when not found.
UpdateAsync(userRef, dto) Updates UserName, Email, FirstName, LastName and PhoneNumber. Fields left null are unchanged. IsActive on the DTO is ignored: use ToggleActivationAsync.
DeleteAsync(userRef) Deletes a user. The record is kept (a soft delete) and all of the user's sessions are revoked.
ToggleActivationAsync(userRef) Toggles the IsActive status.
TogglePasskeyAsync(userRef) Enables/Disables the passkey (FIDO2) requirement.
ToggleTotpAsync(userRef) Enables/Disables TOTP MFA.
ChangePasswordAsync(userRef, old, new) Changes a user's password.
AssignRolesToUserAsync(userRef, roleIds) Bulk assign roles to a user.
UnassignRolesFromUserAsync(userRef, roleIds) Bulk remove roles from a user.
AssignGroupsToUserAsync(userRef, groupIds) Bulk assign groups to a user.
UnassignGroupsFromUserAsync(userRef, groupIds) Bulk remove groups from a user.
AssignPermissionsToUserAsync(userRef, permissionIds) Bulk assign standalone permissions.
UnassignPermissionsFromUserAsync(userRef, permissionIds) Bulk remove standalone permissions.
GetExternalLinkAsync(userRef) Returns your application's external ID for the user plus profile info (CallerLookupDto); null when not found.
LinkExternalIdAsync(userRef, externalUserId) Links a user to your application's external ID.

External ID Lookup

GetByExternalIdAsync(externalId) returns the user (UserDto) mapped to your own identifier, or null. The SDK escapes reserved characters in the id (such as @, & and spaces).

To act on a user you know by your own identifier, look the user up first and pass the returned caller or cid to the method you need:

var user = await client.Users.GetByExternalIdAsync("EMR-USR-44219");
if (user != null)
    await client.Users.ToggleActivationAsync(user.cid);

5. External Mappings (client.Mappings)

Manage the association between HealthPass users and identifiers from external systems. Mappings belong to your application.

Method Description
CreateAsync(dto) Links a user (dto.caller) to a Key/Value pair.
GetByUserAsync(userRef) Fetches all of your application's mappings for a user.
GetByKeyValueAsync(key, value) Looks up a mapping by key and value; null when not found.
DeleteAsync(mappingId) Removes an external mapping. Pass the ExternalMappingDto.Id the API returned, unchanged.

A mapping's Id is an opaque string, like a user's caller. It is not a database number: do not parse it, store it as an integer or compare it numerically. Keep it as returned and hand it back to DeleteAsync.

To link a user to your own id you can also use Users.LinkExternalIdAsync and read it back with Users.GetExternalLinkAsync / Users.GetByExternalIdAsync.


6. Security Entities Management

Groups, roles and permissions created through the SDK belong to your application, and their Code must be unique within it. Deleting is a soft delete.

GetAllAsync on each sub-client takes an optional filter (GroupFilter, RoleFilter, PermissionFilter) with PageIndex, PageSize, IsActive, OrderBy (default CreateStamp), DescendingDirection and KeyWord. Results come one page at a time, so without a filter you receive only the first 10 items. Rows are ordered by the sort field and then by id, so pages never overlap. Page through with PageIndex (pages start at 1) until a page comes back with fewer items than PageSize:

var page2 = await client.Groups.GetAllAsync(new GroupFilter { PageIndex = 2, PageSize = 50, KeyWord = "admin" });

Groups (client.Groups)

  • GetAllAsync([filter]): List groups.
  • GetByIdAsync(id): Fetch a group; null when not found.
  • CreateAsync(dto): Create a group.
  • UpdateAsync(id, dto): Update a group's name, code or description.
  • DeleteAsync(id): Delete a group.
  • AddRolesToGroupAsync(groupId, roleIds): Assign multiple roles to a group.
  • RemoveRolesFromGroupAsync(groupId, roleIds): Remove roles from a group.

Roles (client.Roles)

  • GetAllAsync([filter]): List roles.
  • GetByIdAsync(id): Fetch a role; null when not found.
  • CreateAsync(dto): Create a role.
  • UpdateAsync(id, dto): Update a role's name, code or description.
  • DeleteAsync(id): Delete a role.
  • AddPermissionsToRoleAsync(roleId, permissionIds): Link permissions to a role.
  • RemovePermissionsFromRoleAsync(roleId, permissionIds): Unlink permissions from a role.

Permissions (client.Permissions)

  • GetAllAsync([filter]): List permissions.
  • GetByIdAsync(id): Fetch a permission; null when not found.
  • CreateAsync(dto): Define a new permission code (e.g. REPORT_VIEW).
  • UpdateAsync(id, dto): Update an existing permission.
  • DeleteAsync(id): Delete a permission.

7. Error Handling

Failed calls throw HealthPassSdkException, which subclasses HttpRequestException — existing catch (HttpRequestException ex) code keeps compiling and catching it unchanged. HealthPassSdkException additionally carries:

  • StatusCode — the HTTP status code.
  • Code — the API's machine-readable error code (e.g. "TermsAcceptanceRequired", "NotAssignedToApplication"), or "UnexpectedResponse" when the response wasn't in that shape at all — never null, so you can always branch on Code.
  • Message — the API's human-readable message for that code, never the raw response body or any internal exception detail.

"Not found" is not an error for the Get... methods that return a single item: they return null. A blank userRef or externalId throws ArgumentNullException before any request is made (whitespace-only counts as blank).

try
{
    await client.Auth.LoginAsync(request);
}
catch (HealthPassSdkException ex) when (ex.Code == "UserInactive")
{
    // account is deactivated
}
catch (HealthPassSdkException ex)
{
    Console.WriteLine($"{ex.Code}: {ex.Message}");   // "TermsAcceptanceRequired: ..."
    if (ex.Code == "TermsAcceptanceRequired")
    {
        // The user must accept this application's Terms & Conditions once, in the HealthPass
        // web portal or mobile app, before LoginAsync/LoginEncryptedAsync will succeed for them.
        // The SDK cannot accept terms on a user's behalf — see Step 2 above.
    }
}

A 401 Unauthorized on every call means the request was not accepted as coming from your application: a wrong AppKey/AppSecret, Allow SDK Access disabled, or a server clock that is out of sync. An inactive user account fails LoginAsync/LoginEncryptedAsync with 401 Unauthorized, code UserInactive — not 403.


8. Changelog

The entries below describe API-surface and behavior changes by release, most recent first, together with anything you may need to update in your integration. For questions about a specific release, contact your HealthPass representative.

v1.0.43

Fixed

  • Recreating a Group using the Code of a previously deleted group now succeeds, consistent with Roles and Permissions.
  • An authentication failure on the signed SDK channel — an invalid AppKey/AppSecret, an invalid request signature, an expired timestamp, a replayed nonce, SDK access disabled for the application, no SDK secret configured, or a request body that could not be decrypted — now returns a structured error body with a code to branch on (InvalidAppKey, InvalidSdkSignature, SdkTimestampExpired, SdkNonceReplayed, SdkAccessDisabled, SdkSecretNotConfigured, InvalidEncryptedPayload).
  • Users.CreateAsync/UpdateAsync now validates that an email address contains an @ and a domain, returning 400 with code InvalidEmail for a malformed address.
  • A ValidationFailed response covering multiple fields now separates each message clearly.
  • Password validation failures now carry distinct codes — PasswordRequired, PasswordTooShort, PasswordTooLong — rather than repeating the message as the code. The same applies to a name that does not match the required letters-only pattern: InvalidFirstName/InvalidLastName.

Changed

  • FirstName/LastName in the access token are now issued under ClaimTypes.GivenName/ClaimTypes.Surname, consistent with how other profile claims (email, unique_name, phone_number) are issued. The HealthPass Admin Dashboard's Token Claims tab shows the exact claim key issued in the token for every claim.

v1.0.42

Added

  • Groups.GetAllAsync/Roles.GetAllAsync/Permissions.GetAllAsync support OrderBy: "Code" and "Description", in addition to the existing "Name"/"CreateStamp". An unrecognized value falls back to "CreateStamp".

Fixed

  • LoginEncryptedAsync accepts EncryptedGroupCode: null, not just an omitted or empty value. EncryptedGroupCode is fully optional.
  • LoginEncryptedAsync with fields that fail to decrypt now returns 400 with code InvalidEncryptedPayload — a client error you can branch on.
  • A group-gate rejection (LoginEncryptedAsync with EncryptedGroupCode set, user not a member) now has its own code, NotAGroupMember.
  • Users.AssignGroupsToUserAsync/AssignRolesToUserAsync with an empty list now return a readable message (NoGroupsProvided/NoRolesProvided).
  • Recreating a Role or Permission using the Code of a previously deleted one now succeeds.
  • A field-length violation on Create/Update (for example, a name over its character limit) now returns a readable Message with code ValidationFailed.

v1.0.39

Breaking (narrow)

  • Failed calls now throw HealthPassSdkException instead of a bare HttpRequestException. It derives from HttpRequestException, so catch (HttpRequestException ex) still compiles and still catches it; code that parsed ex.Message for a status or error code should switch to the new StatusCode/Code properties instead (see §7).

Fixed

  • HealthPassSdkException.Code and StatusCode are now always populated on every failure, from every method, so you can rely on them without a null check; Message never contains raw response content.
  • Users.ChangePasswordAsync now rejects an incorrect current password (Code == "IncorrectPassword").
  • Permissions.GetByIdAsync now returns null for an id that does not exist, consistent with Groups.GetByIdAsync and Roles.GetByIdAsync.
  • LoginEncryptedAsync's EncryptedGroupCode is genuinely optional; omit it for a plain encrypted login with no group restriction.
  • Users.GetByExternalIdAsync now returns PhoneNumber and DateOfBirth when your application's claims enable them, consistent with the other user-lookup methods.
  • A blank or whitespace-only userRef/externalId is now rejected client-side (ArgumentNullException) before any request is made.
  • Mappings.DeleteAsync and Mappings.GetByUserAsync work reliably for every mapping id and caller length.
  • Groups.GetAllAsync, Roles.GetAllAsync and Permissions.GetAllAsync are scoped to your own application only.
  • Login distinguishes an inactive account (UserInactive) from a blocked one (UserBlocked) instead of a generic InvalidCredentials.
  • Refresh token handling was hardened: reuse of an already-rotated refresh token is always refused immediately, and a token that was revoked without ever being rotated is reported with its own code (RefreshTokenRevoked), distinct from a detected reuse (RefreshTokenAlreadyUsed).
  • Every client.Groups/Roles/Permissions/Mappings method, including the list methods, consistently surfaces an authentication failure as HealthPassSdkException.
  • UpdateUserDto's UserName, Email, FirstName and LastName are properly optional, matching PhoneNumber — a partial update with only some fields set works as documented.
  • List pagination (GetAllAsync on Groups/Roles/Permissions) behaves correctly for every PageIndex/PageSize value, including 0 or a negative value.
  • Groups.DeleteAsync, Roles.DeleteAsync and Permissions.DeleteAsync report an id that never existed as not found; deleting an already-deleted id remains a no-op success.

Clarified

  • Users.CreateAsync does not guarantee the new user can sign in immediately. If your application has Terms & Conditions configured, LoginAsync/LoginEncryptedAsync answer TermsAcceptanceRequired until the user accepts them once via the HealthPass web portal or mobile app.

v1.0.37

Added

  • Clearer method names for users identified by caller / cid. Users.GetByIdAsync never took an id (unlike Groups, Roles and Permissions, which take an int) — it takes a user's caller or cid. The user methods now say so:

    Previous (still available, marked [Obsolete]) Current
    Users.GetByIdAsync(callerOrCid) Users.GetAsync(userRef)
    Users.GetByCallerAsync(callerOrCid) Users.GetExternalLinkAsync(userRef)
    Users.AssignByCallerAsync(callerOrCid, externalUserId) Users.LinkExternalIdAsync(userRef, externalUserId)
    Mappings.GetByUserIdAsync(caller) Mappings.GetByUserAsync(userRef)

    The previous names forward to the current ones and behave the same at runtime; the compiler flags them until you switch. They will be removed in the next major version.

  • Mappings can be read and deleted. A mapping is identified by an opaque string id: ExternalMappingDto.Id now carries it, so Mappings.CreateAsync, GetByUserAsync and GetByKeyValueAsync return usable mappings and DeleteAsync can remove them.

    • ExternalMappingDto.Id is now a string. Code that reads it as an int (or stores it in an int column) needs a string.
    • Mappings.DeleteAsync(string mappingId) takes that id. DeleteAsync(int) still compiles but is marked [Obsolete].
  • Stable paging: GetAllAsync on groups, roles and permissions orders rows by the sort field and then by id, so consecutive pages never overlap.

  • RsaPublicKeyPem is validated at startup. AddHealthPassSdk throws ArgumentException for a key that is truncated or not a valid RSA public key, rather than on the first encrypted call.

  • Mappings.GetByUserAsync and Mappings.DeleteAsync throw ArgumentNullException for a blank argument before any request is made, like the Users methods.

Changed

  • The callerOrCid parameter is now userRef on every Users method. Positional calls are unaffected; only code that passes the argument by name (callerOrCid: ...) needs the new name. The deprecated methods above keep their previous parameter names.
  • Both caller and cid work at every Users method. Pass whichever you hold; the SDK sends it in the form the API accepts.
  • Users you create are assigned to your application automatically. Users.CreateAsync assigns the new user to your application, so you do not need to look up or pass an application id. CreateUserDto.ApplicationIds has no effect and is marked [Obsolete]. (Sign-in still requires accepting the application's Terms & Conditions first, if it has any — see v1.0.39 above.)
  • The following methods are marked [Obsolete] and are not supported by the API: UpdateByExternalIdAsync, DeleteByExternalIdAsync, ToggleActivationByExternalIdAsync, ChangePasswordByExternalIdAsync, AssignGroupsByExternalIdAsync and Roles.GetPermissionsAsync. Look the user up with GetByExternalIdAsync and use the userRef method with the returned caller or cid; use Permissions.GetAllAsync for permissions.
  • If you implement or mock IUsersSubClient, IMappingsSubClient or IRolesSubClient yourself, add the new members.

v1.0.36

Fixed

  • Request signing now covers the decoded URL path, and external ids are escaped. An external id containing a reserved character (for example an email address, a space or a non-ASCII letter) no longer causes a signature mismatch, and Mappings.GetByKeyValueAsync now escapes its key and value so &, +, # and spaces are preserved.
  • AddHealthPassSdk validates BaseUrl at registration. An invalid, relative, host:port or non-http(s) URL throws ArgumentException at startup rather than when the first client is created.
  • PEM public keys in BEGIN PUBLIC KEY format work on every build. The netstandard2.0 build (used by .NET 6 to .NET 9) previously accepted only BEGIN RSA PUBLIC KEY.

v1.0.34

Added

  • UserDto gains PhoneNumber and DateOfBirth. CreateUserDto gains an optional DateOfBirth you can set at creation time. Both are additive fields.

Fixed

  • Login and user-lookup responses now respect the application's claim masking, not just enabled-claims filtering. LoginAsync/LoginEncryptedAsync (Email/UserName/FirstName/LastName/PhoneNumber) and Users.GetByIdAsync/GetByExternalIdAsync/GetByCallerAsync now return the same masked/filtered values your users' access tokens already carry. If you rely on any of these fields being unmasked, check your application's masking rules in the Admin panel.
  • DateOfBirth is now correctly gated by the birthdate claim: populated when the claim is enabled for your application, null when it's disabled.

v1.0.33

Fixed

  • RefreshAsync's signature is RefreshAsync(string refreshToken); it does not accept an externalUserId argument. To record your own user id, pass ExternalUserId to ExchangeTokenAsync or use Users.AssignByCallerAsync.

v1.0.32

Added

  • cid — a short caller identifier. Every user now has a second opaque identifier alongside the existing caller. cid is a 22-character stable value: the same user always produces the same cid across requests. It appears in the CreateAsync response (UserDto.cid) and in login results (SdkLoginResult.cid).
  • GetByCallerAsync accepts both formats: the long caller (~346 characters) or the short cid (~22 characters) — the server resolves both at the same endpoint.
  • AssignByCallerAsync accepts cid. All caller-based operations accept both formats.

v1.0.31

Fixed

  • ExchangeTokenAsync (PKCE) no longer fails for some requests.

v1.0.30

Added

  • Login audit now captures the caller's IP. Both LoginAsync and LoginEncryptedAsync record the caller's public IP address in the login log. The IP recorded is the address the API sees your server connecting from; pass IpAddress in the request to record your end user's IP instead.

v1.0.29

Breaking

  • RsaPublicKeyPem is now required in the HealthPass configuration section. The request-encryption public key is bound from configuration rather than hard-coded in the SDK, so it can be rotated without a package upgrade. Add the key (provided by HealthPass) to your configuration; AddHealthPassSdk(...) throws at startup otherwise.
  • If you call SdkBodyEncryptor.Encrypt(...) directly (encrypted group-gated login), it now takes the public key as a second argument: Encrypt(plainText, rsaPublicKeyPem).
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
1.0.43 90 9/27/2026
1.0.42 82 9/27/2026
1.0.40 86 9/24/2026
1.0.39 87 9/24/2026
1.0.38 93 9/24/2026
1.0.37 90 9/21/2026
1.0.36 95 9/20/2026
1.0.35 134 7/16/2026
1.0.34 113 7/6/2026
1.0.33 115 7/2/2026
1.0.32 112 6/23/2026
1.0.31 117 6/1/2026
1.0.30 111 6/1/2026
1.0.29 114 5/25/2026
1.0.28 135 5/21/2026
1.0.27 143 5/20/2026
1.0.26 112 5/19/2026
1.0.25 132 5/13/2026
1.0.24 102 5/13/2026
1.0.23 114 5/12/2026
Loading failed