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
<PackageReference Include="LaVie.HealthPass.SDK" Version="1.0.43" />
<PackageVersion Include="LaVie.HealthPass.SDK" Version="1.0.43" />
<PackageReference Include="LaVie.HealthPass.SDK" />
paket add LaVie.HealthPass.SDK --version 1.0.43
#r "nuget: LaVie.HealthPass.SDK, 1.0.43"
#:package LaVie.HealthPass.SDK@1.0.43
#addin nuget:?package=LaVie.HealthPass.SDK&version=1.0.43
#tool nuget:?package=LaVie.HealthPass.SDK&version=1.0.43
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.0andnet10.0. Projects targeting .NET 6 to .NET 9 use thenetstandard2.0build; only .NET 10 uses thenet10.0build.
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
401to 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:
CodeChallengeis 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:
- 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
- After the user signs in with Google, the API redirects the browser to
callbackUrlwith these query parameters:code(the auth code),caller, andplatform(when you sent one). Any query string already oncallbackUrlis kept. - Exchange
codewithExchangeTokenAsync, using the verifier that matches thecodeChallengeyou sent.
Notes:
- The origin of
callbackUrlmust 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_requiredinstead ofcode. - Google sign-in through the SDK does not prompt for the user's HealthPass MFA.
codeChallengeMethoddefaults 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;nullwhen 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;nullwhen 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;nullwhen 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 — nevernull, so you can always branch onCode.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
Codeof 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/UpdateAsyncnow validates that an email address contains an@and a domain, returning400with codeInvalidEmailfor a malformed address.- A
ValidationFailedresponse 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/LastNamein the access token are now issued underClaimTypes.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.GetAllAsyncsupportOrderBy: "Code"and"Description", in addition to the existing"Name"/"CreateStamp". An unrecognized value falls back to"CreateStamp".
Fixed
LoginEncryptedAsyncacceptsEncryptedGroupCode: null, not just an omitted or empty value.EncryptedGroupCodeis fully optional.LoginEncryptedAsyncwith fields that fail to decrypt now returns400with codeInvalidEncryptedPayload— a client error you can branch on.- A group-gate rejection (
LoginEncryptedAsyncwithEncryptedGroupCodeset, user not a member) now has its own code,NotAGroupMember. Users.AssignGroupsToUserAsync/AssignRolesToUserAsyncwith an empty list now return a readable message (NoGroupsProvided/NoRolesProvided).- Recreating a Role or Permission using the
Codeof a previously deleted one now succeeds. - A field-length violation on
Create/Update(for example, a name over its character limit) now returns a readableMessagewith codeValidationFailed.
v1.0.39
Breaking (narrow)
- Failed calls now throw
HealthPassSdkExceptioninstead of a bareHttpRequestException. It derives fromHttpRequestException, socatch (HttpRequestException ex)still compiles and still catches it; code that parsedex.Messagefor a status or error code should switch to the newStatusCode/Codeproperties instead (see §7).
Fixed
HealthPassSdkException.CodeandStatusCodeare now always populated on every failure, from every method, so you can rely on them without a null check;Messagenever contains raw response content.Users.ChangePasswordAsyncnow rejects an incorrect current password (Code == "IncorrectPassword").Permissions.GetByIdAsyncnow returnsnullfor an id that does not exist, consistent withGroups.GetByIdAsyncandRoles.GetByIdAsync.LoginEncryptedAsync'sEncryptedGroupCodeis genuinely optional; omit it for a plain encrypted login with no group restriction.Users.GetByExternalIdAsyncnow returnsPhoneNumberandDateOfBirthwhen your application's claims enable them, consistent with the other user-lookup methods.- A blank or whitespace-only
userRef/externalIdis now rejected client-side (ArgumentNullException) before any request is made. Mappings.DeleteAsyncandMappings.GetByUserAsyncwork reliably for every mapping id andcallerlength.Groups.GetAllAsync,Roles.GetAllAsyncandPermissions.GetAllAsyncare scoped to your own application only.- Login distinguishes an inactive account (
UserInactive) from a blocked one (UserBlocked) instead of a genericInvalidCredentials. - 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/Mappingsmethod, including the list methods, consistently surfaces an authentication failure asHealthPassSdkException. UpdateUserDto'sUserName,Email,FirstNameandLastNameare properly optional, matchingPhoneNumber— a partial update with only some fields set works as documented.- List pagination (
GetAllAsyncon Groups/Roles/Permissions) behaves correctly for everyPageIndex/PageSizevalue, including0or a negative value. Groups.DeleteAsync,Roles.DeleteAsyncandPermissions.DeleteAsyncreport an id that never existed as not found; deleting an already-deleted id remains a no-op success.
Clarified
Users.CreateAsyncdoes not guarantee the new user can sign in immediately. If your application has Terms & Conditions configured,LoginAsync/LoginEncryptedAsyncanswerTermsAcceptanceRequireduntil 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.GetByIdAsyncnever took an id (unlikeGroups,RolesandPermissions, which take anint) — it takes a user'scallerorcid. 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.Idnow carries it, soMappings.CreateAsync,GetByUserAsyncandGetByKeyValueAsyncreturn usable mappings andDeleteAsynccan remove them.ExternalMappingDto.Idis now astring. Code that reads it as anint(or stores it in anintcolumn) needs astring.Mappings.DeleteAsync(string mappingId)takes that id.DeleteAsync(int)still compiles but is marked[Obsolete].
Stable paging:
GetAllAsyncon groups, roles and permissions orders rows by the sort field and then by id, so consecutive pages never overlap.RsaPublicKeyPemis validated at startup.AddHealthPassSdkthrowsArgumentExceptionfor a key that is truncated or not a valid RSA public key, rather than on the first encrypted call.Mappings.GetByUserAsyncandMappings.DeleteAsyncthrowArgumentNullExceptionfor a blank argument before any request is made, like theUsersmethods.
Changed
- The
callerOrCidparameter is nowuserRefon everyUsersmethod. 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
callerandcidwork at everyUsersmethod. Pass whichever you hold; the SDK sends it in the form the API accepts. - Users you create are assigned to your application automatically.
Users.CreateAsyncassigns the new user to your application, so you do not need to look up or pass an application id.CreateUserDto.ApplicationIdshas 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,AssignGroupsByExternalIdAsyncandRoles.GetPermissionsAsync. Look the user up withGetByExternalIdAsyncand use theuserRefmethod with the returnedcallerorcid; usePermissions.GetAllAsyncfor permissions. - If you implement or mock
IUsersSubClient,IMappingsSubClientorIRolesSubClientyourself, 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.GetByKeyValueAsyncnow escapes its key and value so&,+,#and spaces are preserved. AddHealthPassSdkvalidatesBaseUrlat registration. An invalid, relative,host:portor non-http(s)URL throwsArgumentExceptionat startup rather than when the first client is created.- PEM public keys in
BEGIN PUBLIC KEYformat work on every build. Thenetstandard2.0build (used by .NET 6 to .NET 9) previously accepted onlyBEGIN RSA PUBLIC KEY.
v1.0.34
Added
UserDtogainsPhoneNumberandDateOfBirth.CreateUserDtogains an optionalDateOfBirthyou 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) andUsers.GetByIdAsync/GetByExternalIdAsync/GetByCallerAsyncnow 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. DateOfBirthis now correctly gated by thebirthdateclaim: populated when the claim is enabled for your application,nullwhen it's disabled.
v1.0.33
Fixed
RefreshAsync's signature isRefreshAsync(string refreshToken); it does not accept anexternalUserIdargument. To record your own user id, passExternalUserIdtoExchangeTokenAsyncor useUsers.AssignByCallerAsync.
v1.0.32
Added
cid— a short caller identifier. Every user now has a second opaque identifier alongside the existingcaller.cidis a 22-character stable value: the same user always produces the samecidacross requests. It appears in theCreateAsyncresponse (UserDto.cid) and in login results (SdkLoginResult.cid).GetByCallerAsyncaccepts both formats: the longcaller(~346 characters) or the shortcid(~22 characters) — the server resolves both at the same endpoint.AssignByCallerAsyncacceptscid. 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
LoginAsyncandLoginEncryptedAsyncrecord the caller's public IP address in the login log. The IP recorded is the address the API sees your server connecting from; passIpAddressin the request to record your end user's IP instead.
v1.0.29
Breaking
RsaPublicKeyPemis now required in theHealthPassconfiguration 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 | Versions 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. |
-
.NETStandard 2.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Http (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
- System.Net.Http.Json (>= 8.0.0)
- System.Text.Json (>= 10.0.0)
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Http (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
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 |