@healthcloudai/hc-login-connector 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,7 +19,7 @@ below follow the current behavior implemented in `src/client.ts`.
19
19
  6. Password reset initiation and password reset confirmation
20
20
  7. Access token, ID token, tenant, and base URL helper methods
21
21
  8. Token refresh using the stored refresh token
22
- 9. Authenticated patient header retrieval through `getUserInfo()`
22
+ 9. Authenticated patient header retrieval through `getPatientHeader()`
23
23
  10. Built on the shared Healthcheck HttpClient layer
24
24
 
25
25
  ---
@@ -242,14 +242,14 @@ strings. If a field needs to contain multiple values, serialize those values int
242
242
  one string value, such as a comma separated list or another separator-separated
243
243
  list expected by the integration.
244
244
 
245
- `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.
245
+ `isOTP` 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.
246
246
 
247
- - If `verifyEmailCode` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
248
- - If `verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
247
+ - If `isOTP` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
248
+ - If `isOTP` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
249
249
 
250
250
  #### OTP-based registration
251
251
 
252
- Use this when email verification should be handled through an OTP code. In this case, `verifyEmailCode` should be `true`.
252
+ Use this when email verification should be handled through an OTP code. In this case, `isOTP` should be `true`.
253
253
 
254
254
  ```ts
255
255
  await loginClient.register(
@@ -258,7 +258,7 @@ await loginClient.register(
258
258
  "John",
259
259
  "Smith",
260
260
  {
261
- verifyEmailCode: true,
261
+ isOTP: true,
262
262
  attributes: {
263
263
  Specialty: "Cardiology",
264
264
  NPINumber: "1234567890",
@@ -301,7 +301,7 @@ Request sent for the OTP example above:
301
301
 
302
302
  #### Link-based registration
303
303
 
304
- Use this when email verification should be handled through an email link. In this case, `verifyEmailCode` should be omitted or `false`.
304
+ Use this when email verification should be handled through an email link. In this case, `isOTP` should be omitted or `false`.
305
305
 
306
306
  ```ts
307
307
  await loginClient.register(
@@ -354,7 +354,7 @@ Notes:
354
354
  - `attributes` is the extension point for arbitrary additional registration fields.
355
355
  - The module does not need to know custom attribute names in advance.
356
356
  - All `attributes` values should be strings. Multi-value attributes should be serialized into one string value, such as a comma separated list or another integration-specific separator-separated list.
357
- - `verifyEmailCode` takes precedence over any manually provided `VERIFY_EMAIL_CODE` inside `attributes`.
357
+ - `isOTP` takes precedence over any manually provided `VERIFY_EMAIL_CODE` inside `attributes`.
358
358
 
359
359
  ---
360
360
 
@@ -366,12 +366,12 @@ Registers a patient for the configured tenant using the richer registration payl
366
366
 
367
367
  `TenantID` is injected internally from `configure(...)`.
368
368
 
369
- `verifyEmailCode` behaves the same way as in `register(...)`:
369
+ `isOTP` behaves the same way as in `register(...)`:
370
370
  when enabled, the client injects `VERIFY_EMAIL_CODE: "true"` into `User.Attributes`.
371
371
 
372
372
  #### OTP-based registration
373
373
 
374
- Use this when email verification should be handled through an OTP code. In this case, `verifyEmailCode` should be `true`.
374
+ Use this when email verification should be handled through an OTP code. In this case, `isOTP` should be `true`.
375
375
 
376
376
  ```ts
377
377
  await loginClient.registerFull({
@@ -389,7 +389,7 @@ await loginClient.registerFull({
389
389
  genderIdentity: "Male",
390
390
  status: 0,
391
391
  language: "en",
392
- verifyEmailCode: true,
392
+ isOTP: true,
393
393
  attributes: {
394
394
  source: "sdk-test"
395
395
  },
@@ -449,7 +449,7 @@ Request sent for the usage example above:
449
449
 
450
450
  #### Link-based registration
451
451
 
452
- Use this when email verification should be handled through an email link. In this case, `verifyEmailCode` should be omitted or `false`.
452
+ Use this when email verification should be handled through an email link. In this case, `isOTP` should be omitted or `false`.
453
453
 
454
454
  ```ts
455
455
  await loginClient.registerFull({
@@ -510,14 +510,14 @@ verification settings.
510
510
  when email verification should be handled through an OTP code, the client sends
511
511
  `VERIFY_EMAIL_CODE: "true"` inside `User.Attributes`.
512
512
 
513
- - If `options.verifyEmailCode` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
514
- - The value is sent as a string inside `User.Attributes`, matching the `register(...)` method behavior when `verifyEmailCode` is `true`.
515
- - If `options.verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
513
+ - If `options.isOTP` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
514
+ - The value is sent as a string inside `User.Attributes`, matching the `register(...)` method behavior when `isOTP` is `true`.
515
+ - If `options.isOTP` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
516
516
  - Calling `verifyEmail(...)` without `options` keeps OTP email verification disabled by default.
517
517
 
518
518
  #### OTP-based email verification
519
519
 
520
- Use this when email verification should be handled through an OTP code. In this case, `options.verifyEmailCode` should be `true`.
520
+ Use this when email verification should be handled through an OTP code. In this case, `options.isOTP` should be `true`.
521
521
 
522
522
  ```ts
523
523
  await loginClient.verifyEmail(
@@ -525,7 +525,7 @@ await loginClient.verifyEmail(
525
525
  "123456",
526
526
  "en",
527
527
  {
528
- verifyEmailCode: true
528
+ isOTP: true
529
529
  }
530
530
  );
531
531
  ```
@@ -552,7 +552,7 @@ Request sent for the OTP example above:
552
552
 
553
553
  #### Link-based email verification
554
554
 
555
- Use this when email verification should be handled through an email link. In this case, `options.verifyEmailCode` should be omitted or `false`.
555
+ Use this when email verification should be handled through an email link. In this case, `options.isOTP` should be omitted or `false`.
556
556
 
557
557
  ```ts
558
558
  await loginClient.verifyEmail(
@@ -607,21 +607,21 @@ settings.
607
607
  when email verification should be handled through an OTP code, the client sends
608
608
  `VERIFY_EMAIL_CODE: "true"` inside `User.Attributes`.
609
609
 
610
- - If `options.verifyEmailCode` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
611
- - The value is sent as a string inside `User.Attributes`, matching the `register(...)` method behavior when `verifyEmailCode` is `true`.
612
- - If `options.verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
610
+ - If `options.isOTP` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
611
+ - The value is sent as a string inside `User.Attributes`, matching the `register(...)` method behavior when `isOTP` is `true`.
612
+ - If `options.isOTP` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
613
613
  - Calling `resendEmailVerify(...)` without `options` keeps OTP email verification disabled by default.
614
614
 
615
615
  #### OTP-based resend
616
616
 
617
- Use this when email verification should be handled through an OTP code. In this case, `options.verifyEmailCode` should be `true`.
617
+ Use this when email verification should be handled through an OTP code. In this case, `options.isOTP` should be `true`.
618
618
 
619
619
  ```ts
620
620
  await loginClient.resendEmailVerify(
621
621
  "john.smith@example.com",
622
622
  "en",
623
623
  {
624
- verifyEmailCode: true
624
+ isOTP: true
625
625
  }
626
626
  );
627
627
  ```
@@ -648,7 +648,7 @@ Request sent for the OTP example above:
648
648
 
649
649
  #### Link-based resend
650
650
 
651
- Use this when email verification should be handled through an email link. In this case, `options.verifyEmailCode` should be omitted or `false`.
651
+ Use this when email verification should be handled through an email link. In this case, `options.isOTP` should be omitted or `false`.
652
652
 
653
653
  ```ts
654
654
  await loginClient.resendEmailVerify(
@@ -989,20 +989,20 @@ Status:
989
989
 
990
990
  ### Reset Password
991
991
 
992
- `resetPassword` supports both OTP-based and link-based reset initiation
992
+ `requestPasswordReset` supports both OTP-based and link-based reset initiation
993
993
  flows.
994
994
 
995
- `IsPasswordResetWithOTP` is optional. It is included in the request body only
996
- when `isPasswordResetWithOTP` is `true`. When `false` or omitted, the field is
997
- not sent at all and `false` is not serialized into the payload.
995
+ `isOTP` is optional. When it is `true`, the client includes
996
+ `IsPasswordResetWithOTP: true` in the API request. When `false` or omitted, the
997
+ backend field is not sent at all.
998
998
 
999
999
  #### OTP-based password reset initiation
1000
1000
 
1001
1001
  Use this when the password reset flow should be handled through an OTP code.
1002
- In this case, `IsPasswordResetWithOTP` should be `true`.
1002
+ In this case, `isOTP` should be `true`.
1003
1003
 
1004
1004
  ```ts
1005
- await loginClient.resetPassword("john.smith@example.com", true);
1005
+ await loginClient.requestPasswordReset("john.smith@example.com", true);
1006
1006
  ```
1007
1007
 
1008
1008
  #### Full API request
@@ -1020,11 +1020,11 @@ await loginClient.resetPassword("john.smith@example.com", true);
1020
1020
  #### Link-based password reset initiation
1021
1021
 
1022
1022
  Use this when the password reset flow should be handled from an email link.
1023
- In this case, `IsPasswordResetWithOTP` should be omitted by passing `false` or
1023
+ In this case, `isOTP` should be omitted by passing `false` or
1024
1024
  leaving the second argument out.
1025
1025
 
1026
1026
  ```ts
1027
- await loginClient.resetPassword("john.smith@example.com", false);
1027
+ await loginClient.requestPasswordReset("john.smith@example.com", false);
1028
1028
  ```
1029
1029
 
1030
1030
  Effective request body:
@@ -1061,28 +1061,27 @@ Status:
1061
1061
 
1062
1062
  ### Reset Password Confirm
1063
1063
 
1064
- `resetPasswordConfirm` supports both OTP-based and link-based confirmation
1064
+ `confirmPasswordReset` supports both OTP-based and link-based confirmation
1065
1065
  flows.
1066
1066
 
1067
- If `IsPasswordResetWithOTP` is `true`, `Code` is required. If
1068
- `IsPasswordResetWithOTP` is not `true`, `Token` is required. For the
1069
- link-based flow, the frontend is responsible for taking the token from the
1070
- password reset email link and passing that token in the payload.
1067
+ If `isOTP` is `true`, `Code` is required. If `isOTP` is not `true`, `Token` is
1068
+ required. The client maps `isOTP: true` to `IsPasswordResetWithOTP: true` in the
1069
+ API request. For the link-based flow, the frontend is responsible for taking
1070
+ the token from the password reset email link and passing that token in the
1071
+ payload.
1071
1072
 
1072
1073
  #### OTP-based password reset confirmation
1073
1074
 
1074
1075
  Use this when the password reset flow was started with OTP. In this case,
1075
- `IsPasswordResetWithOTP` should be `true` and `Code` should contain the final
1076
+ `isOTP` should be `true` and `Code` should contain the final
1076
1077
  reset code.
1077
1078
 
1078
1079
  ```ts
1079
- await loginClient.resetPasswordConfirm({
1080
- Data: {
1081
- Email: "john.smith@example.com",
1082
- Password: "ExamplePassword123!",
1083
- IsPasswordResetWithOTP: true,
1084
- Code: "123456"
1085
- }
1080
+ await loginClient.confirmPasswordReset({
1081
+ Email: "john.smith@example.com",
1082
+ Password: "ExamplePassword123!",
1083
+ isOTP: true,
1084
+ Code: "123456"
1086
1085
  });
1087
1086
  ```
1088
1087
 
@@ -1105,15 +1104,13 @@ Request body:
1105
1104
  Use this when the password reset flow was started from an email link. In this
1106
1105
  case, `Token` should be included in the payload, the frontend should read the
1107
1106
  token value from the password reset email link and send that token in the
1108
- request payload, and `IsPasswordResetWithOTP` should be omitted or `false`.
1107
+ request payload, and `isOTP` should be omitted or `false`.
1109
1108
 
1110
1109
  ```ts
1111
- await loginClient.resetPasswordConfirm({
1112
- Data: {
1113
- Email: "john.smith@example.com",
1114
- Password: "ExamplePassword123!",
1115
- Token: "token-from-email-link"
1116
- }
1110
+ await loginClient.confirmPasswordReset({
1111
+ Email: "john.smith@example.com",
1112
+ Password: "ExamplePassword123!",
1113
+ Token: "token-from-email-link"
1117
1114
  });
1118
1115
  ```
1119
1116
 
@@ -1240,19 +1237,19 @@ If an API key has been configured through setApiKey(...), the returned header ob
1240
1237
 
1241
1238
  ---
1242
1239
 
1243
- ### Get User Info
1240
+ ### Get Patient Header
1244
1241
 
1245
- Public signature: `loginClient.getUserInfo()`
1242
+ Public signature: `loginClient.getPatientHeader()`
1246
1243
 
1247
1244
  Calls the patient header endpoint and returns the raw server response.
1248
1245
  It requires a stored ID token.
1249
1246
 
1250
1247
  ```ts
1251
- const userInfo = await loginClient.getUserInfo();
1248
+ const header = await loginClient.getPatientHeader();
1252
1249
  ```
1253
1250
 
1254
1251
  #### Full API request
1255
- `getUserInfo()` does not send a request body.
1252
+ `getPatientHeader()` does not send a request body.
1256
1253
 
1257
1254
  #### API response
1258
1255
 
@@ -1266,42 +1263,60 @@ Status:
1266
1263
  {
1267
1264
  "Data": {
1268
1265
  "Record": {
1269
- "ID": "record-id-example",
1270
- "TenantID": "test-tenant",
1266
+ "Status": 1,
1267
+ "Sex": "Male",
1268
+ "GenderIdentity": "Male",
1269
+ "HasInsurance": true,
1270
+ "HasIDCard": true,
1271
+ "HasSelfie": true,
1272
+ "Flags": null,
1271
1273
  "FirstName": "John",
1272
1274
  "LastName": "Smith",
1275
+ "MiddleName": null,
1276
+ "BirthDate": "1990-01-01T00:00:00",
1273
1277
  "Email": "john.smith@example.com",
1274
1278
  "Phone": "+15555550123",
1275
- "BirthDate": "1990-01-01T00:00:00",
1276
- "Gender": "male",
1277
- "Sex": "Male",
1279
+ "Gender": "Male",
1278
1280
  "Race": "White",
1279
- "Ethnicity": "Not Hispanic or Latino",
1280
- "HasInsurance": false,
1281
- "HasIDCard": false,
1282
- "HasSelfie": false,
1281
+ "Ethnicity": "",
1282
+ "CRMID": null,
1283
+ "AppleUserId": null,
1283
1284
  "Address": {
1284
- "StreetAndNumber": "123 Test St",
1285
+ "StreetAndNumber": "1 Main St",
1285
1286
  "Extension": null,
1286
1287
  "City": "Springfield",
1287
- "State": "CA",
1288
- "PostalCode": "90210",
1288
+ "State": "IL",
1289
+ "PostalCode": "62701",
1289
1290
  "Country": "US"
1290
1291
  },
1291
1292
  "Attributes": {
1292
- "FHIRPatientID": "patient-id-example",
1293
- "CompositeID": "composite-id-example",
1294
- "VERIFY_EMAIL_CODE": "true"
1293
+ "AthenaPatientID": "patient-id-example",
1294
+ "AthenaGuarantorFirstName": "John",
1295
+ "AthenaGuarantorLastName": "Smith",
1296
+ "AthenaGuarantorEmail": "john.smith@example.com",
1297
+ "AthenaCountryCode": "USA",
1298
+ "AthenaDepartmentId": "1",
1299
+ "AthenaEmailExists": "True",
1300
+ "AthenaTestPatient": "False",
1301
+ "PatientImageURL": "https://storage.example.com/selfie_example.jpg",
1302
+ "CompositeID": "tenant-id/john.smith@example.com",
1303
+ "Insurance": "EXAMPLE INSURANCE"
1295
1304
  },
1296
- "CompositeID": "composite-id-example",
1297
- "FHIRID": "fhir-id-example"
1305
+ "CompositeID": "tenant-id/john.smith@example.com",
1306
+ "FHIRID": "fhir-id-example",
1307
+ "AthenaID": "patient-id-example",
1308
+ "Created": "0001-01-01T00:00:00",
1309
+ "Modified": "0001-01-01T00:00:00",
1310
+ "CreatedByID": null,
1311
+ "ModifiedByID": null,
1312
+ "IsDeactivated": false,
1313
+ "TenantID": "test-tenant",
1314
+ "ID": "record-uuid-example"
1298
1315
  },
1299
1316
  "Encounters": null,
1300
- "EHR": "fhir",
1317
+ "EHR": "athena",
1301
1318
  "PendingActions": [
1302
- "ADD_INSURANCE",
1303
- "ADD_ID",
1304
- "IMPORT_VITALS"
1319
+ "TAKE_TEST"
1305
1320
  ]
1306
1321
  },
1307
1322
  "ErrorMessage": null,
@@ -1369,10 +1384,47 @@ eyJ_id_token_example
1369
1384
  - Use `submitOnboardingStep(...)` or the onboarding helper methods to complete required onboarding steps.
1370
1385
  - Call `login(...)` to authenticate and store the returned access, refresh, and ID tokens in memory.
1371
1386
  - Use `refreshToken()` to replace the current token set when a stored refresh token is available.
1372
- - Use `getAuthHeader()`, `getAccessToken()`, `getIDToken()`, and `getUserInfo()` for authenticated flows after login.
1387
+ - Use `getAuthHeader()`, `getAccessToken()`, `getIDToken()`, and `getPatientHeader()` for authenticated flows after login.
1373
1388
  - Other Healthcheck connectors can consume `getAuthHeader()` instead of managing auth tokens directly.
1374
1389
 
1375
1390
 
1391
+ ## Token Expiry and Refresh
1392
+
1393
+ The SDK does **not** auto-refresh tokens. Call `refreshToken()` proactively when the token is about to expire.
1394
+
1395
+ `getTokens()` returns the current in-memory token set, including `expiresAt` — an absolute Unix millisecond timestamp parsed from the `Expiration` field the backend returns.
1396
+
1397
+ `isTokenExpired()` is a helper that returns `true` when there is no stored token or when `Date.now() >= expiresAt`.
1398
+
1399
+ Recommended pattern:
1400
+
1401
+ ```ts
1402
+ async function ensureFreshToken(loginClient: HCLoginClient): Promise<void> {
1403
+ if (loginClient.isTokenExpired()) {
1404
+ await loginClient.refreshToken();
1405
+ }
1406
+ }
1407
+
1408
+ // Call before any authenticated connector operation
1409
+ await ensureFreshToken(loginClient);
1410
+ const result = await otherConnector.someMethod();
1411
+ ```
1412
+
1413
+ `getTokens()` shape:
1414
+
1415
+ ```ts
1416
+ interface AuthTokens {
1417
+ accessToken: string;
1418
+ refreshToken: string;
1419
+ idToken: string | null;
1420
+ expiresAt: number; // absolute milliseconds since Unix epoch
1421
+ }
1422
+ ```
1423
+
1424
+ `refreshToken()` does not require a current ID token. It uses the stored refresh token from the previous successful `login()` call.
1425
+
1426
+ ---
1427
+
1376
1428
  ## Notes
1377
1429
 
1378
1430
  - `configure()` stores tenant configuration locally and does not send an HTTP request.
@@ -1380,7 +1432,15 @@ eyJ_id_token_example
1380
1432
  - `register()`, `verifyEmail()`, and `resendEmailVerify()` all follow the same string-based `User.Attributes.VERIFY_EMAIL_CODE` pattern for OTP email verification flows.
1381
1433
  - `saveAddress()` accepts an `Address`.
1382
1434
  - `login()` stores the initial token set internally, and `refreshToken()` updates that stored token set when a refresh token is available.
1383
- - `refreshToken()` does not require a current ID token. It uses the stored refresh token from the previous successful login.
1384
- - `getUserInfo()` calls the patient header endpoint.
1385
- - `getAuthHeader()`, `getAccessToken()`, `getIDToken()`, `getBaseUrl()`, and `getTenantId()` return local values and do not perform HTTP requests.
1435
+ - `getPatientHeader()` calls the patient header endpoint.
1436
+ - `getAuthHeader()` returns `Authorization: Bearer <idToken>` + `X-Tenant-ID`. It does **not** carry an API key — each connector adds its own key independently.
1437
+ - `getAuthHeader()`, `getAccessToken()`, `getIDToken()`, `getBaseUrl()`, `getTenantId()`, `getTokens()`, and `isTokenExpired()` return or evaluate local values and do not perform HTTP requests.
1386
1438
  - This connector is intended to be reused by other Healthcheck SDK connectors built on the same HTTP layer.
1439
+
1440
+ ---
1441
+
1442
+ ## getPatientHeader vs getDashboard
1443
+
1444
+ `HCLoginClient.getPatientHeader()` calls `/api/patient/header` and returns a lightweight patient record plus pending actions. Typically called once after login to check patient state.
1445
+
1446
+ `HCSettingsClient.getDashboard()` (in `hc-settings-connector`) calls `/api/patient/dashboard` and returns a richer dashboard including encounters. These are different endpoints with different response shapes — not duplicates.