npm package: @healthcloudai/hc-login-connector
Description: Healthcheck Login authentication SDK with TypeScrip and token refresh
Author: Healthcheck Systems Inc
Latest version: 0.0.10

# Healthcheck Login Connector

This library provides a tenant-aware client for patient registration, onboarding,
authentication, password reset flows, token refresh, authenticated patient context
retrieval, and local auth helper methods.

The connector is built on top of the shared Healthcheck HTTP layer, and the examples
below follow the current behavior implemented in `src/client.ts`.

---

## Features

1. Patient registration with tenant-scoped request payloads
2. Generic onboarding step submission through `submitOnboardingStep(...)`
3. Convenience helpers for email verification and SMS verification steps
4. Convenience helpers for health profile and address onboarding steps
5. Patient login with in-memory access, refresh, and ID token storage
6. Password reset initiation and password reset confirmation
7. Access token, ID token, tenant, and base URL helper methods
8. Token refresh using the current authenticated token state
9. Authenticated patient header retrieval through `getUserInfo()` and `getHeader()`
10. Built on the shared Healthcheck HttpClient layer

---

## Installation

```sh
npm install @healthcloudai/hc-login-connector \
@healthcloudai/hc-http
```

---

## Import

```ts
import { HCLoginClient } from "@healthcloudai/hc-login-connector";
import { FetchClient } from "@healthcloudai/hc-http";
```

---

## Usage

### Configuration

Call `configure()` before any other method.
It stores the tenant configuration locally on the client instance, and the same
configured client should be reused across registration, onboarding, login, refresh,
and authenticated calls.

```ts
const httpClient = new FetchClient();
const loginClient = new HCLoginClient(httpClient);

loginClient.configure("demo-tenant", "dev");
// The current source signature also accepts an optional region argument:
// loginClient.configure("demo-tenant", "dev", "region-code");
```

#### Full API request

`configure()` does not send an HTTP request.
The configuration example above updates local client state only.

#### API response

`configure()` does not receive an API response.
It stores the tenant configuration locally for later requests.



---

## Methods

### Get Base URL

Public signature: `loginClient.getBaseUrl()`

Returns the base URL derived from the current configuration.

```ts
const baseUrl = loginClient.getBaseUrl();
```

#### Full API request

`getBaseUrl()` does not send an HTTP request.
The usage example above reads the value from local state.

#### API response

`getBaseUrl()` returns the currently configured base URL from local state.

```txt
https://dev-api-healthcheck.healthcloud-services.com/api
```

---

### Get Tenant ID

Public signature: `loginClient.getTenantId()`

Returns the tenant ID currently stored by `configure(...)`.

```ts
const tenantId = loginClient.getTenantId();
```

#### Full API request

`getTenantId()` does not send an HTTP request.
The usage example above reads the value from local state.

#### API response

`getTenantId()` returns the configured tenant ID from local state.

```txt
demo-tenant
```

---

### Submit Onboarding Step

Public signature: `loginClient.submitOnboardingStep(step, user, data?, language?)`

This is the step-driven onboarding API.
The selected `step` determines the final shape of `Data.User`, and `TenantID` is
injected internally from `configure(...)`.
Callers should provide only the fields relevant to the selected step.

Supported steps:

- `EMAIL_VERIFY`
- `RESEND_EMAIL_VERIFY`
- `RESEND_SMS_VERIFY`
- `SMS_VERIFY`
- `SAVE_HEALTH_PROFILE`
- `SAVE_ADDRESS`

```ts
await loginClient.submitOnboardingStep(
  "SAVE_HEALTH_PROFILE",
  {
    Email: "john.doe@example.com",
    FirstName: "John",
    LastName: "Doe",
    BirthDate: "1990-01-01",
    Gender: "male",
    Race: "White",
    Ethnicity: "Not Hispanic or Latino",
    Status: 0,
    Sex: "Male"
  },
  "",
  "en"
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "",
    "Step": "SAVE_HEALTH_PROFILE",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "FirstName": "John",
      "LastName": "Doe",
      "BirthDate": "1990-01-01",
      "Gender": "male",
      "Sex": "Male",
      "Status": 0,
      "Race": "White",
      "Ethnicity": "Not Hispanic or Latino"
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Register

Public signature: `loginClient.register(email, password, firstName?, lastName?, options?)`

Registers a patient for the configured tenant.

`TenantID` is injected internally from `configure(...)`.

`options.attributes` can be used to pass arbitrary additional custom fields.
Every key/value pair from `options.attributes` is forwarded into `User.Attributes`.
This is the extension point for arbitrary extra registration fields, so the module
does not need to know those field names in advance.

`verifyEmailCode` is a dedicated boolean option that controls whether `User.Attributes.VERIFY_EMAIL_CODE` is included in the registration payload, depending on whether email verification should be handled through an OTP code.

- If `verifyEmailCode` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
- If `verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.

```ts
await loginClient.register(
  "john.doe@example.com",
  "ExamplePassword123!",
  "John",
  "Doe",
  {
    verifyEmailCode: true,
    attributes: {
      specialty: "Cardiology",
      NPINumber: "1234567890",
      FullName: "John Doe",
      PracticeLegalName: "Acme Medical Group"
    }
  }
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "TenantID": "demo-tenant",
    "Credentials": {
      "Email": "john.doe@example.com",
      "Password": "ExamplePassword123!"
    },
    "User": {
      "FirstName": "John",
      "LastName": "Doe",
      "Email": "john.doe@example.com",
      "Attributes": {
        "specialty": "Cardiology",
        "NPINumber": "1234567890",
        "FullName": "John Doe",
        "PracticeLegalName": "Acme Medical Group",
        "VERIFY_EMAIL_CODE": "true"
      }
    }
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

Notes:

- `TenantID` is taken from the configured client instance.
- `attributes` is the extension point for arbitrary additional registration fields.
- The module does not need to know custom attribute names in advance.
- `verifyEmailCode` takes precedence over any manually provided `VERIFY_EMAIL_CODE` inside `attributes`.

---

### Verify Email

Public signature: `loginClient.verifyEmail(email, code, language?)`

Convenience wrapper around `submitOnboardingStep(...)`.
The caller provides `email`, `code`, and an optional `language`, and the client
adds `VERIFY_EMAIL_CODE: "true"` internally.

```ts
await loginClient.verifyEmail(
  "john.doe@example.com",
  "123456",
  "en"
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "123456",
    "Step": "EMAIL_VERIFY",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "Attributes": {
        "VERIFY_EMAIL_CODE": "true"
      }
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Resend Email Verify

Public signature: `loginClient.resendEmailVerify(email, language?)`

Convenience wrapper around `submitOnboardingStep(...)`.
The caller provides `email` and an optional `language`, and the client adds
`VERIFY_EMAIL_CODE: "true"` internally.

```ts
await loginClient.resendEmailVerify(
  "john.doe@example.com",
  "en"
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "",
    "Step": "RESEND_EMAIL_VERIFY",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "Attributes": {
        "VERIFY_EMAIL_CODE": "true"
      }
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Resend SMS Verify

Public signature: `loginClient.resendSmsVerify(email, phone, language?)`

Convenience wrapper around `submitOnboardingStep(...)`.
The caller provides `email`, `phone`, and an optional `language`.

```ts
await loginClient.resendSmsVerify(
  "john.doe@example.com",
  "+15555550123",
  "en"
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "",
    "Step": "RESEND_SMS_VERIFY",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "Phone": "+15555550123"
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Verify SMS

Public signature: `loginClient.verifySms(email, phone, code, language?)`

Convenience wrapper around `submitOnboardingStep(...)`.
The caller provides `email`, `phone`, `code`, and an optional `language`.

```ts
await loginClient.verifySms(
  "john.doe@example.com",
  "+15555550123",
  "123456",
  "en"
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "123456",
    "Step": "SMS_VERIFY",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "Phone": "+15555550123"
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Save Health Profile

Public signature: `loginClient.saveHealthProfile(profile, language?)`

Convenience wrapper around `submitOnboardingStep(...)`.
The caller provides the `SAVE_HEALTH_PROFILE` input fields, such as `Email`,
`FirstName`, `LastName`, `BirthDate`, `Gender`, `Race`, `Ethnicity`, `Status`,
and `Sex`.

```ts
await loginClient.saveHealthProfile({
  Email: "john.doe@example.com",
  FirstName: "John",
  LastName: "Doe",
  BirthDate: "1990-01-01",
  Gender: "male",
  Race: "White",
  Ethnicity: "Not Hispanic or Latino",
  Status: 0,
  Sex: "Male"
});
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "",
    "Step": "SAVE_HEALTH_PROFILE",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "FirstName": "John",
      "LastName": "Doe",
      "BirthDate": "1990-01-01",
      "Gender": "male",
      "Sex": "Male",
      "Status": 0,
      "Race": "White",
      "Ethnicity": "Not Hispanic or Latino"
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Save Address

Public signature: `loginClient.saveAddress(email, address, language?)`

Convenience wrapper around `submitOnboardingStep(...)`.
The caller provides `email`, an `Address`, and an optional `language`.

```ts
await loginClient.saveAddress("john.doe@example.com", {
  StreetAndNumber: "123 Main St",
  Extension: null,
  City: "Springfield",
  State: "CA",
  PostalCode: "90210",
  Country: "US"
});
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Data": "",
    "Step": "SAVE_ADDRESS",
    "User": {
      "Email": "john.doe@example.com",
      "TenantID": "demo-tenant",
      "Address": {
        "StreetAndNumber": "123 Main St",
        "Extension": null,
        "City": "Springfield",
        "State": "CA",
        "PostalCode": "90210",
        "Country": "US"
      }
    },
    "Language": "en"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": "",
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Login

Public signature: `loginClient.login(email, password)`

`login(...)` accepts only `email` and `password`.
The client builds the full backend `Data` payload internally, and stores the
returned tokens in memory.
On tenants that require onboarding completion, login may depend on the relevant
onboarding steps already being finished.

```ts
await loginClient.login(
  "john.doe@example.com",
  "ExamplePassword123!"
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "FirstName": "",
    "LastName": "",
    "Email": "john.doe@example.com",
    "Password": "ExamplePassword123!",
    "TenantID": "demo-tenant",
    "AppPoolID": "",
    "GoogleIdToken": "",
    "AppleIdToken": "",
    "AppleCode": ""
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": {
    "Email": "john.doe@example.com",
    "RefreshToken": "eyJ_refresh_token_example",
    "AccessToken": "eyJ_access_token_example",
    "IDToken": "eyJ_id_token_example",
    "EHR": "fhir",
    "HasInsurance": false,
    "HasIDCard": false,
    "HasSelfie": false,
    "Attributes": {
      "EHR": "fhir"
    },
    "TenantID": "demo-tenant",
    "Expiration": "2030-01-01T00:00:00.000Z",
    "Type": 0
  },
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Reset Password

Public signature: `loginClient.resetPassword(email, isPasswordResetWithOTP?)`

`TenantID` is injected automatically from `configure(...)`.
If an ID token is already available, the client may also include an
`Authorization` header.

```ts
await loginClient.resetPassword(
  "john.doe@example.com",
  true
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Email": "john.doe@example.com",
    "TenantID": "demo-tenant",
    "IsPasswordResetWithOTP": true
  }
}
```

`Authorization: Bearer eyJ_id_token_example` may also be included when an ID token
is already stored.

#### API response

Status:

```txt
200
```

```json
{
  "Data": true,
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Reset Password Confirm

Public signature: `loginClient.resetPasswordConfirm(email, password, code, isPasswordResetWithOTP?)`

`TenantID` is injected automatically from `configure(...)`.
If an ID token is already available, the client may also include an
`Authorization` header.

```ts
await loginClient.resetPasswordConfirm(
  "john.doe@example.com",
  "ExamplePassword123!",
  "123456",
  true
);
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "Email": "john.doe@example.com",
    "TenantID": "demo-tenant",
    "Password": "ExamplePassword123!",
    "code": "123456",
    "IsPasswordResetWithOTP": true
  }
}
```

`Authorization: Bearer eyJ_id_token_example` may also be included when an ID token
is already stored.

#### API response

Status:

```txt
200
```

```json
{
  "Data": true,
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Refresh Token

Public signature: `loginClient.refreshToken()`

`refreshToken()` requires a successful prior login and an available refresh token.
The client sends the stored refresh token, and stores the returned token set in
memory.

```ts
await loginClient.refreshToken();
```

#### Full API request
Request sent for the usage example above:

```json
{
  "Data": {
    "RefreshToken": "eyJ_refresh_token_example",
    "TenantID": "demo-tenant"
  }
}
```

#### API response

Status:

```txt
200
```

```json
{
  "Data": {
    "Email": null,
    "RefreshToken": "eyJ_refresh_token_example",
    "AccessToken": "eyJ_access_token_example",
    "IDToken": "eyJ_id_token_example",
    "TenantID": "demo-tenant",
    "Expiration": "2030-01-01T00:00:00.000Z",
    "Type": 0
  },
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Get Auth Header

Public signature: `loginClient.getAuthHeader()`

Returns the authorization headers required for authenticated API requests.
This method does not send an HTTP request, and other Healthcheck connectors can
reuse the returned header object directly.

```ts
const headers = loginClient.getAuthHeader();
```

#### Full API request

`getAuthHeader()` does not send an HTTP request.
The usage example above reads the value from local state.

#### API response

`getAuthHeader()` returns the current local header object.

```json
{
  "Authorization": "Bearer eyJ_id_token_example",
  "X-Tenant-ID": "demo-tenant"
}
```

---

### Get User Info

Public signature: `loginClient.getUserInfo()`

The current implementation calls the patient header endpoint and returns the raw
server response.
It requires a stored ID token, and it currently targets the same endpoint as
`getHeader()`.

```ts
const userInfo = await loginClient.getUserInfo();
```

#### Full API request
`getUserInfo()` does not send a request body.

#### API response

Status:

```txt
200
```

```json
{
  "Data": {
    "Record": {
      "ID": "record-id-example",
      "TenantID": "demo-tenant",
      "FirstName": "John",
      "LastName": "Doe",
      "Email": "john.doe@example.com",
      "Phone": "+15555550123",
      "BirthDate": "1990-01-01T00:00:00",
      "Gender": "male",
      "Sex": "Male",
      "Race": "White",
      "Ethnicity": "Not Hispanic or Latino",
      "HasInsurance": false,
      "HasIDCard": false,
      "HasSelfie": false,
      "Address": {
        "StreetAndNumber": "123 Main St",
        "Extension": null,
        "City": "Springfield",
        "State": "CA",
        "PostalCode": "90210",
        "Country": "US"
      },
      "Attributes": {
        "FHIRPatientID": "patient-id-example",
        "CompositeID": "composite-id-example",
        "VERIFY_EMAIL_CODE": "true"
      },
      "CompositeID": "composite-id-example",
      "FHIRID": "fhir-id-example"
    },
    "Encounters": null,
    "EHR": "fhir",
    "PendingActions": [
      "ADD_INSURANCE",
      "ADD_ID",
      "IMPORT_VITALS"
    ]
  },
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Get Header

Public signature: `loginClient.getHeader()`

The current implementation also calls the patient header endpoint.
It requires a stored ID token, and the response shape is effectively the same as
`getUserInfo()`.

```ts
await loginClient.getHeader();
```

#### Full API request
`getHeader()` does not send a request body.

#### API response

Status:

```txt
200
```

```json
{
  "Data": {
    "Record": {
      "ID": "record-id-example",
      "TenantID": "demo-tenant",
      "FirstName": "John",
      "LastName": "Doe",
      "Email": "john.doe@example.com",
      "Phone": "+15555550123",
      "BirthDate": "1990-01-01T00:00:00",
      "Gender": "male",
      "Sex": "Male",
      "Race": "White",
      "Ethnicity": "Not Hispanic or Latino",
      "HasInsurance": false,
      "HasIDCard": false,
      "HasSelfie": false,
      "Address": {
        "StreetAndNumber": "123 Main St",
        "Extension": null,
        "City": "Springfield",
        "State": "CA",
        "PostalCode": "90210",
        "Country": "US"
      },
      "Attributes": {
        "FHIRPatientID": "patient-id-example",
        "CompositeID": "composite-id-example",
        "VERIFY_EMAIL_CODE": "true"
      },
      "CompositeID": "composite-id-example",
      "FHIRID": "fhir-id-example"
    },
    "Encounters": null,
    "EHR": "fhir",
    "PendingActions": [
      "ADD_INSURANCE",
      "ADD_ID",
      "IMPORT_VITALS"
    ]
  },
  "ErrorMessage": null,
  "IsOK": true
}
```

---

### Get Access Token

Public signature: `loginClient.getAccessToken()`

Returns the current in-memory access token.

```ts
const accessToken = loginClient.getAccessToken();
```

#### Full API request

`getAccessToken()` does not send an HTTP request.
The usage example above reads the value from local state.

#### API response

`getAccessToken()` returns the current local access token value.

```txt
eyJ_access_token_example
```

---

### Get ID Token

Public signature: `loginClient.getIDToken()`

Returns the current in-memory ID token.

```ts
const idToken = loginClient.getIDToken();
```

#### Full API request

`getIDToken()` does not send an HTTP request.
The usage example above reads the value from local state.

#### API response

`getIDToken()` returns the current local ID token value.

```txt
eyJ_id_token_example
```

---

## How It Works

- Create a shared `HttpClient` instance and a single `HCLoginClient`.
- Call `configure(...)` once to store tenant and environment settings locally.
- Call `register(...)` if the patient account needs to be created.
- Use `submitOnboardingStep(...)` or the onboarding helper methods to complete required onboarding steps.
- Call `login(...)` to authenticate and store the returned access, refresh, and ID tokens in memory.
- Use `refreshToken()` to replace the current token set when needed.
- Use `getAuthHeader()`, `getAccessToken()`, `getIDToken()`, `getUserInfo()`, and `getHeader()` for authenticated flows after login.
- Other Healthcheck connectors can consume `getAuthHeader()` instead of managing auth tokens directly.


## Notes

- `configure()` stores tenant configuration locally and does not send an HTTP request.
- `TenantID` is injected by the client into tenant-bound register, onboarding, login, refresh, and password requests.
- `register()` serializes `verifyEmailCode` into `User.Attributes.VERIFY_EMAIL_CODE`, while `verifyEmail()` and `resendEmailVerify()` set `VERIFY_EMAIL_CODE: "true"` internally.
- `saveAddress()` accepts an `Address`.
- `login()` and `refreshToken()` both store returned tokens internally for later helper calls.
- `getUserInfo()` and `getHeader()` both currently call the patient header endpoint.
- `getAuthHeader()`, `getAccessToken()`, `getIDToken()`, `getBaseUrl()`, and `getTenantId()` return local values and do not perform HTTP requests.
- This connector is intended to be reused by other Healthcheck SDK connectors built on the same HTTP layer.