gdc-sdk-node-ts 2.4.2 → 2.4.3

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.
@@ -21,6 +21,21 @@ export type EmployeeDeviceActivationResult = {
21
21
  exchange: SubmitAndPollResult;
22
22
  dcr: SubmitAndPollResult;
23
23
  };
24
+ export type EmployeeDeviceRevocationInput = {
25
+ licenseId: string;
26
+ clientId: string;
27
+ requestThid?: string;
28
+ pollOptions?: PollOptions;
29
+ };
30
+ export declare function revokeEmployeeDeviceWithDeps(deps: {
31
+ routeCtx: RouteContext;
32
+ input: EmployeeDeviceRevocationInput;
33
+ identityDeviceRevokePath: (ctx: RouteContext) => string;
34
+ identityDeviceRevokePollPath: (ctx: RouteContext) => string;
35
+ submitAndPoll: (submitPath: string, pollPath: string, payload: {
36
+ thid?: string;
37
+ } & Record<string, unknown>, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
38
+ }): Promise<SubmitAndPollResult>;
24
39
  type ActivateEmployeeDeviceDeps = {
25
40
  routeCtx: RouteContext;
26
41
  input: EmployeeDeviceActivationInput;
@@ -1,30 +1,44 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { resolvePollOptionsFromSeconds } from './poll-options.js';
3
+ import { IdentityAuthRequestFields, IdentityAuthResponseFields, IdentityDcrMetadataFields, IdentityDeviceInfoFields, } from 'gdc-common-utils-ts/constants/identity-auth';
4
+ import { buildEmployeeDeviceRevocationBody } from 'gdc-common-utils-ts/utils/organization-employee-lifecycle';
5
+ export async function revokeEmployeeDeviceWithDeps(deps) {
6
+ const licenseId = String(deps.input.licenseId || '').trim();
7
+ const clientId = String(deps.input.clientId || '').trim();
8
+ if (!licenseId || !clientId)
9
+ throw new Error('revokeEmployeeDevice: licenseId and clientId are required.');
10
+ return deps.submitAndPoll(deps.identityDeviceRevokePath(deps.routeCtx), deps.identityDeviceRevokePollPath(deps.routeCtx), {
11
+ thid: deps.input.requestThid || `device-revoke-${createRuntimeUuid()}`,
12
+ body: buildEmployeeDeviceRevocationBody({ licenseId, clientId }),
13
+ }, deps.input.pollOptions);
14
+ }
3
15
  export async function activateEmployeeDeviceWithActivationCodeWithDeps(deps) {
4
- const deviceInfo = deps.input.dcrPayload.ext_device_info;
5
- const redirectUris = deps.input.dcrPayload.redirect_uris;
6
- const jwks = deps.input.dcrPayload.jwks;
7
- const clientInstanceId = String(deviceInfo?.device_id
8
- || (deps.input.dcrPayload.software_id ? `software:${deps.input.dcrPayload.software_id}` : '')
16
+ const deviceInfo = deps.input.dcrPayload[IdentityDcrMetadataFields.ExtendedDeviceInfo];
17
+ const redirectUris = deps.input.dcrPayload[IdentityDcrMetadataFields.RedirectUris];
18
+ const jwks = deps.input.dcrPayload[IdentityDcrMetadataFields.Jwks];
19
+ const clientInstanceId = String(deviceInfo?.[IdentityDeviceInfoFields.DeviceId]
20
+ || (deps.input.dcrPayload[IdentityDcrMetadataFields.SoftwareId] ? `software:${deps.input.dcrPayload[IdentityDcrMetadataFields.SoftwareId]}` : '')
9
21
  || (redirectUris?.[0] ? `redirect:${redirectUris[0]}` : '')
10
22
  || (jwks?.keys?.[0]?.kid ? `key:${jwks.keys[0].kid}` : '')).trim();
11
23
  if (!clientInstanceId)
12
24
  throw new Error('Device activation requires a stable device, software, redirect, or key identifier.');
13
25
  const exchangePayload = {
14
26
  thid: `exchange-${createRuntimeUuid()}`,
15
- subject_token: deps.input.activationCode,
16
- client_instance_id: clientInstanceId,
27
+ [IdentityAuthRequestFields.SubjectToken]: deps.input.activationCode,
28
+ [IdentityAuthRequestFields.ClientInstanceId]: clientInstanceId,
17
29
  };
18
30
  const exchange = await deps.submitAndPollWithBearerToken(deps.input.idToken, deps.identityTokenExchangePath(deps.routeCtx), deps.identityTokenExchangePollPath(deps.routeCtx), exchangePayload, deps.input.pollOptions);
19
31
  const pollBody = exchange.poll.body || {};
20
32
  const exchangeBody = (pollBody.body || pollBody);
21
- const initialAccessToken = String(exchangeBody.initial_access_token || exchangeBody.access_token || '').trim();
33
+ const initialAccessToken = String(exchangeBody[IdentityAuthResponseFields.InitialAccessToken]
34
+ || exchangeBody[IdentityAuthResponseFields.AccessToken]
35
+ || '').trim();
22
36
  if (!initialAccessToken) {
23
37
  throw new Error('activateEmployeeDeviceWithActivationCode: missing initial_access_token in exchange response.');
24
38
  }
25
39
  const dcrPayload = {
26
40
  thid: `dcr-${createRuntimeUuid()}`,
27
- code: deps.input.activationCode,
41
+ [IdentityAuthRequestFields.Code]: deps.input.activationCode,
28
42
  ...deps.input.dcrPayload,
29
43
  };
30
44
  const dcr = await deps.submitAndPollWithBearerToken(initialAccessToken, deps.identityDeviceDcrPath(deps.routeCtx), deps.identityDeviceDcrPollPath(deps.routeCtx), dcrPayload, deps.input.pollOptions);
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export * from './individual-onboarding.js';
18
18
  export * from './device-activation.js';
19
19
  export * from './organization-controller-recovery.js';
20
20
  export * from './organization-registration-authorization.js';
21
+ export * from './organization-employee-lifecycle.js';
21
22
  export * from './smart-token.js';
22
23
  export * from './fhir-r5-subscription-runtime.js';
23
24
  export * from './order-offer-summary.js';
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ export * from './individual-onboarding.js';
19
19
  export * from './device-activation.js';
20
20
  export * from './organization-controller-recovery.js';
21
21
  export * from './organization-registration-authorization.js';
22
+ export * from './organization-employee-lifecycle.js';
22
23
  export * from './smart-token.js';
23
24
  export * from './fhir-r5-subscription-runtime.js';
24
25
  export * from './order-offer-summary.js';
@@ -8,12 +8,14 @@ import { type IndividualOrganizationConfirmOrderInput, type RouteContext } from
8
8
  import { type EnsureFamilyOrganizationRegistrationInput } from './family-organization-registration.js';
9
9
  import { type FamilyOrganizationSearchInput } from './family-organization-search.js';
10
10
  import { type SmartTokenRequestInput } from './smart-token.js';
11
- import { type EmployeeDeviceActivationRequestInput, type EmployeeDeviceActivationResult } from './device-activation.js';
11
+ import { type EmployeeDeviceActivationRequestInput, type EmployeeDeviceActivationResult, type EmployeeDeviceRevocationInput } from './device-activation.js';
12
12
  import { type OrganizationLicenseOrderConfirmInput } from './organization-license-order.js';
13
13
  import { type IndividualOrganizationBootstrapInput, type IndividualOrganizationStartResult } from './individual-start.js';
14
- import { type CommunicationIngestionInput, type BlockchainArtifactRegistrationInput, type VitalSignBatchCommunicationFromSearchResponseInput, type CommunicationParticipantRuntimeSearchInput, type ClinicalBundleSearchInput, type ClinicalSectionUpdateInput, type ClinicalSummaryReadResult, type ClinicalSummaryRequestInput, type ClinicalSummaryUpdateInput, type GrantProfessionalAccessInput, type GrantProfessionalAccessResult, type IndividualMemberLifecycleInput, type IndividualMemberLicenseAddInput, type IndividualMemberLicenseInvitationInput, type IndividualMemberLicenseTransitionInput, type LicenseListRuntimeSearchInput, type LicenseOfferRuntimeSearchInput, type LicenseOrderRuntimeSearchInput, type OrganizationEmployeeCreationInput, type OrganizationEmployeeLifecycleInput, type OrganizationEmployeeSearchInput, type ProfessionalAccessRequestInput, type ProfessionalAccessRequestResult, type RevokeProfessionalAccessInput, type RevokeProfessionalAccessResult, type RelatedPersonUpsertInput } from './resource-operations.js';
14
+ import { type CommunicationIngestionInput, type BlockchainArtifactRegistrationInput, type VitalSignBatchCommunicationFromSearchResponseInput, type CommunicationParticipantRuntimeSearchInput, type ClinicalBundleSearchInput, type ClinicalSectionUpdateInput, type ClinicalSummaryReadResult, type ClinicalSummaryRequestInput, type ClinicalSummaryUpdateInput, type GrantProfessionalAccessInput, type GrantProfessionalAccessResult, type IndividualMemberLifecycleInput, type IndividualMemberLicenseAddInput, type IndividualMemberLicenseInvitationInput, type IndividualMemberLicenseTransitionInput, type LicenseListRuntimeSearchInput, type LicenseOfferRuntimeSearchInput, type LicenseOrderRuntimeSearchInput, type OrganizationEmployeeCreationInput, type OrganizationEmployeeLicenseInvitationInput, type OrganizationEmployeeLifecycleInput, type OrganizationEmployeeSearchInput, type ProfessionalAccessRequestInput, type ProfessionalAccessRequestResult, type RevokeProfessionalAccessInput, type RevokeProfessionalAccessResult, type RelatedPersonUpsertInput } from './resource-operations.js';
15
15
  import type { LegalOrganizationOrderInput } from './host-onboarding.js';
16
16
  import type { SmartTokenExchangeResult } from './smart-token.js';
17
+ import { type OrganizationEmployeeProvisioningInput, type OrganizationEmployeeProvisioningResult } from './organization-employee-lifecycle.js';
18
+ import type { OrganizationEmployeeLifecycleRecord } from 'gdc-common-utils-ts/models/organization-employee-lifecycle';
17
19
  import type { NodeRuntimeClient, PollOptions, PollResult, SubmitAndPollResult, SubmitPayload, SubmitResponse } from './orchestration/client-port.js';
18
20
  import { type FhirR5SubscriptionBatchInput } from './fhir-r5-subscription-runtime.js';
19
21
  import type { FhirR5SubscriptionTopic } from 'gdc-common-utils-ts/models/fhir-r5-subscription';
@@ -250,6 +252,12 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
250
252
  * Creates an employee or professional entry under an existing organization tenant.
251
253
  */
252
254
  createOrganizationEmployee(ctx: RouteContext, input: OrganizationEmployeeCreationInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
255
+ /** Creates an employee and reserves its activation credential as one SDK workflow. */
256
+ provisionOrganizationEmployee(ctx: RouteContext, input: OrganizationEmployeeProvisioningInput): Promise<OrganizationEmployeeProvisioningResult>;
257
+ /** Revokes one selected employee installation without releasing its seat. */
258
+ revokeEmployeeDevice(ctx: RouteContext, input: EmployeeDeviceRevocationInput): Promise<SubmitAndPollResult>;
259
+ /** Reserves an employee seat and issues its bounded multi-device activation credential. */
260
+ issueOrganizationEmployeeLicense(ctx: RouteContext, input: OrganizationEmployeeLicenseInvitationInput): Promise<SubmitAndPollResult>;
253
261
  /**
254
262
  * Disables an employee using the current GW CORE contract.
255
263
  *
@@ -276,6 +284,8 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
276
284
  * Searches employees/professionals under the selected organization tenant.
277
285
  */
278
286
  searchOrganizationEmployees(ctx: RouteContext, input: OrganizationEmployeeSearchInput): Promise<SubmitAndPollResult>;
287
+ /** Lists employees with their license and installation state. */
288
+ listOrganizationEmployeeLifecycle(ctx: RouteContext): Promise<OrganizationEmployeeLifecycleRecord[]>;
279
289
  /**
280
290
  * Searches organization-owned license seats through the canonical
281
291
  * `License/_search` route.
@@ -8,11 +8,12 @@ import { confirmIndividualOrganizationOrderWithDeps, } from './individual-onboar
8
8
  import { ensureFamilyOrganizationRegistrationWithDeps, } from './family-organization-registration.js';
9
9
  import { searchFamilyOrganizationWithDeps } from './family-organization-search.js';
10
10
  import { requestSmartTokenWithDeps } from './smart-token.js';
11
- import { activateEmployeeDeviceWithActivationCodeWithDeps, activateEmployeeDeviceWithActivationRequestWithDeps, } from './device-activation.js';
11
+ import { activateEmployeeDeviceWithActivationCodeWithDeps, activateEmployeeDeviceWithActivationRequestWithDeps, revokeEmployeeDeviceWithDeps, } from './device-activation.js';
12
12
  import { extractOfferIdFromResponseBody, extractOfferPreviewFromResponseBody, } from './order-offer-summary.js';
13
13
  import { confirmOrganizationLicenseOrderWithDeps } from './organization-license-order.js';
14
14
  import { startIndividualOrganizationWithDeps } from './individual-start.js';
15
- import { createOrganizationEmployeeWithDeps, disableIndividualMemberWithDeps, disableIndividualOrganizationWithDeps, disableOrganizationEmployeeWithDeps, listIndividualLicenseOffersWithDeps, listIndividualLicenseOrdersWithDeps, listIndividualLicensesWithDeps, addFreeIndividualMemberLicensesWithDeps, issueIndividualMemberLicenseWithDeps, transitionIndividualMemberLicenseWithDeps, listOrganizationLicenseOffersWithDeps, listOrganizationLicenseOrdersWithDeps, listOrganizationLicensesWithDeps, grantProfessionalAccessWithDeps, buildClinicalSectionUpdateIngestion, buildClinicalSummaryUpdateIngestion, ingestCommunicationAndUpdateIndexWithDeps, requestProfessionalAccessWithDeps, buildVitalSignBatchCommunicationFromSearchResponse, registerBlockchainArtifactAndUpdateIndexWithDeps, requestClinicalSummaryWithDeps, revokeProfessionalAccessWithDeps, searchCommunicationParticipantsWithDeps, purgeIndividualMemberWithDeps, purgeIndividualOrganizationWithDeps, purgeOrganizationEmployeeWithDeps, prepareEmployeeLifecycleMessageForTransport, searchIndividualLicensesWithDeps, searchIndividualLicenseOffersWithDeps, searchIndividualLicenseOrdersWithDeps, searchOrganizationLicensesWithDeps, searchOrganizationLicenseOffersWithDeps, searchOrganizationLicenseOrdersWithDeps, searchOrganizationEmployeesWithDeps, searchClinicalBundleWithDeps, searchLatestIpsWithDeps, upsertRelatedPersonAndPollWithDeps, normalizeCommunicationPathFormatSegment, } from './resource-operations.js';
15
+ import { createOrganizationEmployeeWithDeps, issueOrganizationEmployeeLicenseWithDeps, disableIndividualMemberWithDeps, disableIndividualOrganizationWithDeps, disableOrganizationEmployeeWithDeps, listIndividualLicenseOffersWithDeps, listIndividualLicenseOrdersWithDeps, listIndividualLicensesWithDeps, addFreeIndividualMemberLicensesWithDeps, issueIndividualMemberLicenseWithDeps, transitionIndividualMemberLicenseWithDeps, listOrganizationLicenseOffersWithDeps, listOrganizationLicenseOrdersWithDeps, listOrganizationLicensesWithDeps, grantProfessionalAccessWithDeps, buildClinicalSectionUpdateIngestion, buildClinicalSummaryUpdateIngestion, ingestCommunicationAndUpdateIndexWithDeps, requestProfessionalAccessWithDeps, buildVitalSignBatchCommunicationFromSearchResponse, registerBlockchainArtifactAndUpdateIndexWithDeps, requestClinicalSummaryWithDeps, revokeProfessionalAccessWithDeps, searchCommunicationParticipantsWithDeps, purgeIndividualMemberWithDeps, purgeIndividualOrganizationWithDeps, purgeOrganizationEmployeeWithDeps, prepareEmployeeLifecycleMessageForTransport, searchIndividualLicensesWithDeps, searchIndividualLicenseOffersWithDeps, searchIndividualLicenseOrdersWithDeps, searchOrganizationLicensesWithDeps, searchOrganizationLicenseOffersWithDeps, searchOrganizationLicenseOrdersWithDeps, searchOrganizationEmployeesWithDeps, searchClinicalBundleWithDeps, searchLatestIpsWithDeps, upsertRelatedPersonAndPollWithDeps, normalizeCommunicationPathFormatSegment, } from './resource-operations.js';
16
+ import { listOrganizationEmployeeLifecycleWithDeps, provisionOrganizationEmployeeWithDeps, } from './organization-employee-lifecycle.js';
16
17
  import { submitAndPollWithMethods } from './orchestration/client-port.js';
17
18
  import { buildRuntimeHeaders, fetchWithTimeout, parseResponseBody, pollBatchResponseWithRuntimeConfig, postJsonWithRuntimeConfig, postRenderedWithRuntimeConfig, } from './runtime-transport.js';
18
19
  import { activateOrganizationInGatewayFromIcaProofWithDeps, submitLegalOrganizationCredentialReissuanceWithDeps, submitLegalOrganizationVerificationTransactionWithDeps, submitOrganizationDidBindingWithDeps, } from './runtime-host-submission.js';
@@ -365,6 +366,35 @@ export class HttpRuntimeClient {
365
366
  submitAndPoll: (submitPath, pollPath, payload, options) => this.submitClinicalMessageAndPoll(submitPath, pollPath, prepareEmployeeLifecycleMessageForTransport(payload, this.transportProfile), this.transportProfile, options),
366
367
  });
367
368
  }
369
+ /** Creates an employee and reserves its activation credential as one SDK workflow. */
370
+ async provisionOrganizationEmployee(ctx, input) {
371
+ return provisionOrganizationEmployeeWithDeps(ctx, input, {
372
+ createEmployee: (routeContext, creation) => this.createOrganizationEmployee(routeContext, creation),
373
+ issueLicense: (routeContext, invitation) => this.issueOrganizationEmployeeLicense(routeContext, invitation),
374
+ });
375
+ }
376
+ /** Revokes one selected employee installation without releasing its seat. */
377
+ async revokeEmployeeDevice(ctx, input) {
378
+ return revokeEmployeeDeviceWithDeps({
379
+ routeCtx: ctx,
380
+ input,
381
+ identityDeviceRevokePath: this.paths.identityDeviceRevokePath.bind(this.paths),
382
+ identityDeviceRevokePollPath: this.paths.identityDeviceRevokePollPath.bind(this.paths),
383
+ submitAndPoll: this.transportProfile === TransportProfiles.DidcommEncryptedForm
384
+ ? (submitPath, pollPath, payload, pollOptions) => this.submitClinicalMessageAndPoll(submitPath, pollPath, payload, this.transportProfile, pollOptions)
385
+ : this.submitAndPoll.bind(this),
386
+ });
387
+ }
388
+ /** Reserves an employee seat and issues its bounded multi-device activation credential. */
389
+ async issueOrganizationEmployeeLicense(ctx, input) {
390
+ return issueOrganizationEmployeeLicenseWithDeps(ctx, input, {
391
+ identityLicenseIssuePath: this.paths.identityLicenseIssuePath.bind(this.paths),
392
+ identityLicenseIssuePollPath: this.paths.identityLicenseIssuePollPath.bind(this.paths),
393
+ submitAndPoll: this.transportProfile === TransportProfiles.DidcommEncryptedForm
394
+ ? (submitPath, pollPath, payload, pollOptions) => this.submitClinicalMessageAndPoll(submitPath, pollPath, payload, this.transportProfile, pollOptions)
395
+ : this.submitAndPoll.bind(this),
396
+ });
397
+ }
368
398
  /**
369
399
  * Disables an employee using the current GW CORE contract.
370
400
  *
@@ -413,6 +443,13 @@ export class HttpRuntimeClient {
413
443
  submitAndPoll: this.submitAndPoll.bind(this),
414
444
  });
415
445
  }
446
+ /** Lists employees with their license and installation state. */
447
+ async listOrganizationEmployeeLifecycle(ctx) {
448
+ return listOrganizationEmployeeLifecycleWithDeps(ctx, {
449
+ searchEmployees: (routeContext, input) => this.searchOrganizationEmployees(routeContext, input),
450
+ listLicenses: (routeContext, input) => this.listOrganizationLicenses(routeContext, input),
451
+ });
452
+ }
416
453
  /**
417
454
  * Searches organization-owned license seats through the canonical
418
455
  * `License/_search` route.
@@ -5,7 +5,9 @@ import type { IndividualOrganizationLifecycleInput, OrganizationDidBindingInput
5
5
  import type { LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput } from '../resource-operations.js';
6
6
  import type { AsyncPollRequest, OrganizationActivationServiceOptions, PollOptions, PollResult, SubmitAndPollResult, SubmitPayload, SubmitResponse } from 'gdc-sdk-core-ts';
7
7
  export type { AsyncPollRequest, PollOptions, PollResult, SubmitAndPollResult, SubmitPayload, SubmitResponse, } from 'gdc-sdk-core-ts';
8
- import type { EmployeeDeviceActivationResult, EmployeeDeviceActivationRequestInput } from '../device-activation.js';
8
+ import type { EmployeeDeviceActivationResult, EmployeeDeviceActivationRequestInput, EmployeeDeviceRevocationInput } from '../device-activation.js';
9
+ import type { OrganizationEmployeeProvisioningInput, OrganizationEmployeeProvisioningResult } from '../organization-employee-lifecycle.js';
10
+ import type { OrganizationEmployeeLifecycleRecord } from 'gdc-common-utils-ts/models/organization-employee-lifecycle';
9
11
  import type { HostRouteContext, HostedTenantLifecycleInput, LegalOrganizationOrderInput } from '../host-onboarding.js';
10
12
  import type { IndividualOrganizationConfirmOrderInput, RouteContext } from '../individual-onboarding.js';
11
13
  import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from '../individual-start.js';
@@ -15,7 +17,7 @@ import type { FhirR5SubscriptionBatchInput } from '../fhir-r5-subscription-runti
15
17
  import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizationRegistrationResult } from '../family-organization-registration.js';
16
18
  import type { OrganizationLicenseOrderConfirmInput } from '../organization-license-order.js';
17
19
  import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
18
- import type { CommunicationIngestionInput, BlockchainArtifactRegistrationInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, VitalSignBatchCommunicationFromSearchResponseInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput, ProfessionalAccessRequestInput, ProfessionalAccessRequestResult, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
20
+ import type { CommunicationIngestionInput, BlockchainArtifactRegistrationInput, CommunicationParticipantRuntimeSearchInput, ClinicalBundleSearchInput, ClinicalSectionUpdateInput, ClinicalSummaryReadResult, ClinicalSummaryRequestInput, ClinicalSummaryUpdateInput, VitalSignBatchCommunicationFromSearchResponseInput, DigitalTwinGenerationInput, GrantProfessionalAccessInput, GrantProfessionalAccessResult, IndividualMemberLifecycleInput, IndividualMemberLicenseAddInput, IndividualMemberLicenseInvitationInput, IndividualMemberLicenseTransitionInput, IpsOrFhirImportInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLicenseInvitationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput, ProfessionalAccessRequestInput, ProfessionalAccessRequestResult, RevokeProfessionalAccessInput, RevokeProfessionalAccessResult, RelatedPersonUpsertInput } from '../resource-operations.js';
19
21
  /**
20
22
  * Shared node-runtime activation input.
21
23
  *
@@ -72,7 +74,10 @@ export type RuntimeClient = {
72
74
  disableTenant?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
73
75
  purgeTenant?: (hostCtx: HostRouteContext, input: HostedTenantLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
74
76
  createOrganizationEmployee?: (ctx: RouteContext, input: OrganizationEmployeeCreationInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
77
+ provisionOrganizationEmployee?: (ctx: RouteContext, input: OrganizationEmployeeProvisioningInput) => Promise<OrganizationEmployeeProvisioningResult>;
78
+ issueOrganizationEmployeeLicense?: (ctx: RouteContext, input: OrganizationEmployeeLicenseInvitationInput) => Promise<SubmitAndPollResult>;
75
79
  searchOrganizationEmployees?: (ctx: RouteContext, input: OrganizationEmployeeSearchInput) => Promise<SubmitAndPollResult>;
80
+ listOrganizationEmployeeLifecycle?: (ctx: RouteContext) => Promise<OrganizationEmployeeLifecycleRecord[]>;
76
81
  searchOrganizationLicenses?: (ctx: RouteContext, input: LicenseListRuntimeSearchInput) => Promise<SubmitAndPollResult>;
77
82
  listOrganizationLicenses?: (ctx: RouteContext, input?: LicenseListRuntimeSearchInput) => Promise<SubmitAndPollResult>;
78
83
  searchOrganizationLicenseOffers?: (ctx: RouteContext, input: LicenseOfferRuntimeSearchInput) => Promise<SubmitAndPollResult>;
@@ -85,6 +90,7 @@ export type RuntimeClient = {
85
90
  disableOrganizationEmployee?: (ctx: RouteContext, input: OrganizationEmployeeLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
86
91
  purgeOrganizationEmployee?: (ctx: RouteContext, input: OrganizationEmployeeLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
87
92
  activateEmployeeDeviceWithActivationRequest?: (input: EmployeeDeviceActivationRequestInput) => Promise<EmployeeDeviceActivationResult>;
93
+ revokeEmployeeDevice?: (ctx: RouteContext, input: EmployeeDeviceRevocationInput) => Promise<SubmitAndPollResult>;
88
94
  requestSmartToken?: (input: SmartTokenRequestInput) => Promise<SmartTokenExchangeResult>;
89
95
  startIndividualOrganization?: (input: IndividualOrganizationBootstrapInput) => Promise<IndividualOrganizationStartResult>;
90
96
  searchFamilyOrganization?: (ctx: RouteContext, input: FamilyOrganizationSearchInput) => Promise<FamilyOrganizationSummary | null>;
@@ -1,11 +1,13 @@
1
1
  import { type NodeOrganizationDidBindingInput, type NodeLegalOrganizationVerificationTransactionInput, type NodeRuntimeClient, type PollOptions, type SubmitAndPollResult, type SubmitPayload } from './client-port.js';
2
2
  import type { RouteContext } from '../individual-onboarding.js';
3
3
  import type { HostRouteContext, HostedTenantLifecycleInput } from '../host-onboarding.js';
4
- import type { EmployeeDeviceActivationResult, EmployeeDeviceActivationRequestInput } from '../device-activation.js';
4
+ import type { EmployeeDeviceActivationResult, EmployeeDeviceActivationRequestInput, EmployeeDeviceRevocationInput } from '../device-activation.js';
5
5
  import type { SmartTokenExchangeResult, SmartTokenRequestInput } from '../smart-token.js';
6
6
  import type { OrganizationLicenseOrderConfirmInput } from '../organization-license-order.js';
7
7
  import type { NodeCapability } from '../session.js';
8
- import type { LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput } from '../resource-operations.js';
8
+ import type { LicenseListRuntimeSearchInput, LicenseOfferRuntimeSearchInput, LicenseOrderRuntimeSearchInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLicenseInvitationInput, OrganizationEmployeeLifecycleInput, OrganizationEmployeeSearchInput } from '../resource-operations.js';
9
+ import type { OrganizationEmployeeProvisioningInput, OrganizationEmployeeProvisioningResult } from '../organization-employee-lifecycle.js';
10
+ import type { OrganizationEmployeeLifecycleRecord } from 'gdc-common-utils-ts/models/organization-employee-lifecycle';
9
11
  /**
10
12
  * Organization-controller oriented facade over a `NodeRuntimeClient`.
11
13
  *
@@ -55,6 +57,10 @@ export declare class OrganizationControllerSdk {
55
57
  * Creates an employee/professional under the current organization tenant.
56
58
  */
57
59
  createOrganizationEmployee(ctx: RouteContext, input: OrganizationEmployeeCreationInput, pollOptions?: PollOptions): Promise<SubmitAndPollResult>;
60
+ /** Creates the employee and issues its reusable multi-installation credential. */
61
+ provisionOrganizationEmployee(ctx: RouteContext, input: OrganizationEmployeeProvisioningInput): Promise<OrganizationEmployeeProvisioningResult>;
62
+ /** Reserves the employee seat and returns its bounded multi-device activation credential. */
63
+ issueOrganizationEmployeeLicense(ctx: RouteContext, input: OrganizationEmployeeLicenseInvitationInput): Promise<SubmitAndPollResult>;
58
64
  /**
59
65
  * Disables an employee using the current GW CORE lifecycle contract.
60
66
  */
@@ -67,6 +73,8 @@ export declare class OrganizationControllerSdk {
67
73
  * Searches employees/professionals under the current organization tenant.
68
74
  */
69
75
  searchOrganizationEmployees(ctx: RouteContext, input: OrganizationEmployeeSearchInput): Promise<SubmitAndPollResult>;
76
+ /** Lists employees with typed license and device lifecycle state. */
77
+ listOrganizationEmployeeLifecycle(ctx: RouteContext): Promise<OrganizationEmployeeLifecycleRecord[]>;
70
78
  /**
71
79
  * Searches organization-owned license seats using semantic license filters.
72
80
  */
@@ -130,6 +138,8 @@ export declare class OrganizationControllerSdk {
130
138
  * Activates the employee device from a previously issued activation request.
131
139
  */
132
140
  activateEmployeeDeviceWithActivationRequest(input: EmployeeDeviceActivationRequestInput): Promise<EmployeeDeviceActivationResult>;
141
+ /** Revokes one selected installation while preserving the employee and seat. */
142
+ revokeEmployeeDevice(ctx: RouteContext, input: EmployeeDeviceRevocationInput): Promise<SubmitAndPollResult>;
133
143
  /**
134
144
  * Requests a SMART token for the current organization-scoped actor.
135
145
  *
@@ -66,6 +66,15 @@ export class OrganizationControllerSdk {
66
66
  assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationCreateEmployee, ActorKinds.OrganizationController, 'createOrganizationEmployee');
67
67
  return requireClientMethod(this.client, 'createOrganizationEmployee')(ctx, input, pollOptions);
68
68
  }
69
+ /** Creates the employee and issues its reusable multi-installation credential. */
70
+ provisionOrganizationEmployee(ctx, input) {
71
+ assertFacadeCapability(this.capabilities, ActorCapabilities.OrganizationCreateEmployee, ActorKinds.OrganizationController, 'provisionOrganizationEmployee');
72
+ return requireClientMethod(this.client, 'provisionOrganizationEmployee')(ctx, input);
73
+ }
74
+ /** Reserves the employee seat and returns its bounded multi-device activation credential. */
75
+ issueOrganizationEmployeeLicense(ctx, input) {
76
+ return requireClientMethod(this.client, 'issueOrganizationEmployeeLicense')(ctx, input);
77
+ }
69
78
  /**
70
79
  * Disables an employee using the current GW CORE lifecycle contract.
71
80
  */
@@ -86,6 +95,10 @@ export class OrganizationControllerSdk {
86
95
  searchOrganizationEmployees(ctx, input) {
87
96
  return requireClientMethod(this.client, 'searchOrganizationEmployees')(ctx, input);
88
97
  }
98
+ /** Lists employees with typed license and device lifecycle state. */
99
+ listOrganizationEmployeeLifecycle(ctx) {
100
+ return requireClientMethod(this.client, 'listOrganizationEmployeeLifecycle')(ctx);
101
+ }
89
102
  /**
90
103
  * Searches organization-owned license seats using semantic license filters.
91
104
  */
@@ -177,6 +190,10 @@ export class OrganizationControllerSdk {
177
190
  activateEmployeeDeviceWithActivationRequest(input) {
178
191
  return requireClientMethod(this.client, 'activateEmployeeDeviceWithActivationRequest')(input);
179
192
  }
193
+ /** Revokes one selected installation while preserving the employee and seat. */
194
+ revokeEmployeeDevice(ctx, input) {
195
+ return requireClientMethod(this.client, 'revokeEmployeeDevice')(ctx, input);
196
+ }
180
197
  /**
181
198
  * Requests a SMART token for the current organization-scoped actor.
182
199
  *
@@ -0,0 +1,64 @@
1
+ import { type EmployeeActivationGrant, type OrganizationEmployeeLifecycleRecord } from 'gdc-common-utils-ts';
2
+ import type { RouteContext } from './individual-onboarding.js';
3
+ import type { SubmitAndPollResult } from './orchestration/client-port.js';
4
+ import type { ServerProfileEnrollmentInput, ServerProfileRecord } from './server-profile-session.js';
5
+ import type { LicenseListRuntimeSearchInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLicenseInvitationInput, OrganizationEmployeeSearchInput } from './resource-operations.js';
6
+ /** Input for the atomic controller-facing create plus seat issuance workflow. */
7
+ export type OrganizationEmployeeProvisioningInput = Readonly<{
8
+ creation: OrganizationEmployeeCreationInput;
9
+ invitation: OrganizationEmployeeLicenseInvitationInput;
10
+ }>;
11
+ /** Results required by a portal to persist routing and display the credential once. */
12
+ export type OrganizationEmployeeProvisioningResult = Readonly<{
13
+ employee: SubmitAndPollResult;
14
+ license: SubmitAndPollResult;
15
+ activationCode: string;
16
+ }>;
17
+ /** Shared dependencies for the create plus seat issuance workflow. */
18
+ export type OrganizationEmployeeProvisioningDeps = Readonly<{
19
+ createEmployee(routeContext: RouteContext, input: OrganizationEmployeeCreationInput): Promise<SubmitAndPollResult>;
20
+ issueLicense(routeContext: RouteContext, input: OrganizationEmployeeLicenseInvitationInput): Promise<SubmitAndPollResult>;
21
+ }>;
22
+ /** Creates the employee, reserves its seat and returns the exact GW credential. */
23
+ export declare function provisionOrganizationEmployeeWithDeps(routeContext: RouteContext, input: OrganizationEmployeeProvisioningInput, deps: OrganizationEmployeeProvisioningDeps): Promise<OrganizationEmployeeProvisioningResult>;
24
+ /** Shared dependencies for combining employee and license directory searches. */
25
+ export type OrganizationEmployeeLifecycleQueryDeps = Readonly<{
26
+ searchEmployees(routeContext: RouteContext, input: OrganizationEmployeeSearchInput): Promise<SubmitAndPollResult>;
27
+ listLicenses(routeContext: RouteContext, input?: LicenseListRuntimeSearchInput): Promise<SubmitAndPollResult>;
28
+ }>;
29
+ /** Lists employees and their installation allowance through one typed projection. */
30
+ export declare function listOrganizationEmployeeLifecycleWithDeps(routeContext: RouteContext, deps: OrganizationEmployeeLifecycleQueryDeps): Promise<OrganizationEmployeeLifecycleRecord[]>;
31
+ /** Input used to persist routing metadata for one issued employee credential. */
32
+ export type EmployeeActivationGrantInput = Readonly<{
33
+ email: string;
34
+ employeeDid: string;
35
+ employeeRoleCode: string;
36
+ providerDid: string;
37
+ routeContext: RouteContext;
38
+ createdAt: Date;
39
+ expiresAt: Date;
40
+ }>;
41
+ /** Builds the product-neutral routing grant stored under a credential digest. */
42
+ export declare function createEmployeeActivationGrant(input: EmployeeActivationGrantInput): EmployeeActivationGrant;
43
+ /** Verifies that a stored routing grant belongs to the authenticated email. */
44
+ export declare function employeeActivationGrantMatchesEmail(grant: EmployeeActivationGrant, email: string): boolean;
45
+ /** Stable profile id derived from account, employee and provider identities. */
46
+ export declare function buildOrganizationEmployeeProfileId(input: Readonly<{
47
+ ownerId: string;
48
+ employeeDid: string;
49
+ providerDid: string;
50
+ clientInstanceId: string;
51
+ }>): string;
52
+ /** Enrolls a previously invited employee without organization discovery. */
53
+ export declare function enrollInvitedOrganizationEmployeeWithDeps(input: Readonly<{
54
+ ownerId: string;
55
+ idToken: string;
56
+ activationCode: string;
57
+ pin: string;
58
+ grant: EmployeeActivationGrant;
59
+ dcrRedirectUris: string[];
60
+ dcrClientName: string;
61
+ clientInstanceId: string;
62
+ }>, deps: Readonly<{
63
+ enroll(enrollment: ServerProfileEnrollmentInput): Promise<ServerProfileRecord>;
64
+ }>): Promise<ServerProfileRecord>;
@@ -0,0 +1,76 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { createHash } from 'node:crypto';
3
+ import { ActorKinds, EmployeeActivationGrantVersions, projectOrganizationEmployeeLifecycle, readEmployeeActivationCode, } from 'gdc-common-utils-ts';
4
+ import { buildStableActorIdentifier } from 'gdc-common-utils-ts/utils/actor-identifier';
5
+ /** Creates the employee, reserves its seat and returns the exact GW credential. */
6
+ export async function provisionOrganizationEmployeeWithDeps(routeContext, input, deps) {
7
+ const employee = await deps.createEmployee(routeContext, input.creation);
8
+ const license = await deps.issueLicense(routeContext, input.invitation);
9
+ const activationCode = readEmployeeActivationCode(license.poll.body);
10
+ if (!activationCode) {
11
+ throw new Error('provisionOrganizationEmployee: GW response did not contain an employee activation credential.');
12
+ }
13
+ return { employee, license, activationCode };
14
+ }
15
+ /** Lists employees and their installation allowance through one typed projection. */
16
+ export async function listOrganizationEmployeeLifecycleWithDeps(routeContext, deps) {
17
+ const [employees, licenses] = await Promise.all([
18
+ deps.searchEmployees(routeContext, {}),
19
+ deps.listLicenses(routeContext),
20
+ ]);
21
+ return projectOrganizationEmployeeLifecycle({
22
+ employeeResponse: employees.poll.body,
23
+ licenseResponse: licenses.poll.body,
24
+ });
25
+ }
26
+ /** Builds the product-neutral routing grant stored under a credential digest. */
27
+ export function createEmployeeActivationGrant(input) {
28
+ return {
29
+ version: EmployeeActivationGrantVersions.V1,
30
+ employeeDid: input.employeeDid,
31
+ employeeRoleCode: input.employeeRoleCode,
32
+ employeeActorIdentifier: buildStableActorIdentifier({ contactKind: 'email', contact: input.email }),
33
+ providerDid: input.providerDid,
34
+ routeContext: input.routeContext,
35
+ createdAt: input.createdAt.toISOString(),
36
+ expiresAt: input.expiresAt.toISOString(),
37
+ };
38
+ }
39
+ /** Verifies that a stored routing grant belongs to the authenticated email. */
40
+ export function employeeActivationGrantMatchesEmail(grant, email) {
41
+ return grant.employeeActorIdentifier === buildStableActorIdentifier({ contactKind: 'email', contact: email });
42
+ }
43
+ /** Stable profile id derived from account, employee and provider identities. */
44
+ export function buildOrganizationEmployeeProfileId(input) {
45
+ const digest = createHash('sha256')
46
+ .update([input.ownerId, input.employeeDid, input.providerDid, input.clientInstanceId].join('\u0000'))
47
+ .digest('base64url');
48
+ return `organization-employee:${digest}`;
49
+ }
50
+ /** Enrolls a previously invited employee without organization discovery. */
51
+ export async function enrollInvitedOrganizationEmployeeWithDeps(input, deps) {
52
+ const profileId = buildOrganizationEmployeeProfileId({
53
+ ownerId: input.ownerId,
54
+ employeeDid: input.grant.employeeDid,
55
+ providerDid: input.grant.providerDid,
56
+ clientInstanceId: input.clientInstanceId,
57
+ });
58
+ return deps.enroll({
59
+ ownerId: input.ownerId,
60
+ profileId,
61
+ actorKind: ActorKinds.OrganizationEmployee,
62
+ actorMode: 'member',
63
+ actorDid: input.grant.employeeDid,
64
+ profileDid: input.grant.employeeDid,
65
+ providerDid: input.grant.providerDid,
66
+ routeContext: input.grant.routeContext,
67
+ allowedSubjectDids: [input.grant.employeeDid],
68
+ pin: input.pin,
69
+ idToken: input.idToken,
70
+ activationCode: input.activationCode,
71
+ clientInstanceId: input.clientInstanceId,
72
+ dcrRedirectUris: input.dcrRedirectUris,
73
+ dcrClientName: input.dcrClientName,
74
+ vpToken: input.idToken,
75
+ });
76
+ }
@@ -1,4 +1,4 @@
1
- import type { DeviceAppType } from 'gdc-common-utils-ts/constants/device';
1
+ import { type DeviceAppType } from 'gdc-common-utils-ts/constants/device';
2
2
  import { type ClinicalSectionUpdateCommunicationInput, type ClinicalUpdateCommunicationInput, type ClinicalSummaryReadResult, type ClinicalSummaryRequestInput, type PermissionRequestCommunicationInput } from 'gdc-sdk-core-ts';
3
3
  import type { LicenseOfferSearchState, LicenseOrderSearchState } from 'gdc-common-utils-ts/utils/license-commercial-search';
4
4
  import type { LicenseListSearchState } from 'gdc-common-utils-ts/utils/license-list-search';
@@ -28,6 +28,18 @@ export type OrganizationEmployeeCreationInput = {
28
28
  employeeClaims: Record<string, unknown>;
29
29
  dataType?: string;
30
30
  };
31
+ /** Input for reserving an existing employee seat and issuing its activation credential. */
32
+ export type OrganizationEmployeeLicenseInvitationInput = {
33
+ email: string;
34
+ role: string;
35
+ subjectDid: string;
36
+ type?: DeviceAppType;
37
+ requestThid?: string;
38
+ pollOptions?: {
39
+ timeoutMs?: number;
40
+ intervalMs?: number;
41
+ };
42
+ };
31
43
  /**
32
44
  * Current GW CORE employee lifecycle locator payload.
33
45
  *
@@ -489,6 +501,21 @@ export declare function createOrganizationEmployeeWithDeps(routeCtx: RouteContex
489
501
  intervalMs?: number;
490
502
  }) => Promise<SubmitAndPollResult>;
491
503
  }): Promise<SubmitAndPollResult>;
504
+ /**
505
+ * Issues the activation credential for an employee already created under the
506
+ * tenant. The credential belongs to that employee seat and can authorize its
507
+ * configured number of concurrent installations (two by default in GW CORE).
508
+ */
509
+ export declare function issueOrganizationEmployeeLicenseWithDeps(routeCtx: RouteContext, input: OrganizationEmployeeLicenseInvitationInput, deps: {
510
+ identityLicenseIssuePath: (ctx: RouteContext) => string;
511
+ identityLicenseIssuePollPath: (ctx: RouteContext) => string;
512
+ submitAndPoll: (submitPath: string, pollPath: string, payload: {
513
+ thid?: string;
514
+ } & Record<string, unknown>, pollOptions?: {
515
+ timeoutMs?: number;
516
+ intervalMs?: number;
517
+ }) => Promise<SubmitAndPollResult>;
518
+ }): Promise<SubmitAndPollResult>;
492
519
  export declare function disableOrganizationEmployeeWithDeps(routeCtx: RouteContext, input: OrganizationEmployeeLifecycleInput, options: {
493
520
  timeoutMs?: number;
494
521
  intervalMs?: number;
@@ -5,6 +5,7 @@ import { RelatedPersonClaim } from 'gdc-common-utils-ts/models/interoperable-cla
5
5
  import { CommunicationClaim } from 'gdc-common-utils-ts/models/interoperable-claims/communication-claims';
6
6
  import { BundleEditor, buildCommunicationParticipantSearchBundle, buildBlockchainArtifactDocumentReference, BundleQuery, createInteroperableResourceOperationEditor, IndividualOrganizationLifecycleEditor, LicenseOfferSearchEditor, LicenseOrderSearchEditor, InteroperableLifecycleStatuses, LicenseListSearchEditor, buildLicenseIssueEntry, buildLicensePurchaseEntry, } from 'gdc-common-utils-ts';
7
7
  import { EmployeeBundleOperations, EmployeeResourceTypes, } from 'gdc-common-utils-ts/utils/employee';
8
+ import { DeviceAppTypes, DeviceUserClasses } from 'gdc-common-utils-ts/constants/device';
8
9
  import { addFhirResourceToCommunication, createCommunicationResource, buildClinicalSummaryCommunicationJob, buildPermissionRequestCommunication, createClinicalSectionUpdateOutboxJob, createClinicalSummaryUpdateOutboxJob, readClinicalSummaryOperationResult, TransportProfiles, } from 'gdc-sdk-core-ts';
9
10
  import { GwCoreLifecycleRequestMethod, GwCoreLifecycleRequestType, GwCoreLifecycleTodo, } from './constants/lifecycle.js';
10
11
  import { buildEmployeeSearchBundle, ConsentClaims, } from 'gdc-sdk-core-ts';
@@ -58,6 +59,37 @@ export async function createOrganizationEmployeeWithDeps(routeCtx, input, option
58
59
  });
59
60
  return deps.submitAndPoll(deps.employeeBatchPath(routeCtx), deps.employeePollPath(routeCtx), payload, options);
60
61
  }
62
+ /**
63
+ * Issues the activation credential for an employee already created under the
64
+ * tenant. The credential belongs to that employee seat and can authorize its
65
+ * configured number of concurrent installations (two by default in GW CORE).
66
+ */
67
+ export async function issueOrganizationEmployeeLicenseWithDeps(routeCtx, input, deps) {
68
+ const email = String(input.email || '').trim().toLowerCase();
69
+ const role = String(input.role || '').trim();
70
+ const subjectDid = String(input.subjectDid || '').trim();
71
+ if (!email)
72
+ throw new Error('issueOrganizationEmployeeLicense: email is required.');
73
+ if (!role)
74
+ throw new Error('issueOrganizationEmployeeLicense: role is required.');
75
+ if (!subjectDid)
76
+ throw new Error('issueOrganizationEmployeeLicense: subjectDid is required.');
77
+ const entry = buildLicenseIssueEntry({
78
+ email,
79
+ role,
80
+ userClass: DeviceUserClasses.Employee,
81
+ type: input.type || DeviceAppTypes.Web,
82
+ });
83
+ entry.meta.subjectDid = subjectDid;
84
+ return deps.submitAndPoll(deps.identityLicenseIssuePath(routeCtx), deps.identityLicenseIssuePollPath(routeCtx), {
85
+ thid: input.requestThid || `employee-license-issue-${createRuntimeUuid()}`,
86
+ body: {
87
+ resourceType: EmployeeResourceTypes.bundle,
88
+ type: EmployeeResourceTypes.batch,
89
+ data: [entry],
90
+ },
91
+ }, input.pollOptions);
92
+ }
61
93
  export async function disableOrganizationEmployeeWithDeps(routeCtx, input, options, deps) {
62
94
  // TODO(gw-core-lifecycle-target-patch-employee-disable): switch this
63
95
  // legacy DELETE-in-_batch flow to `_batch + PATCH` when GW CORE deploys it.
@@ -88,6 +88,10 @@ export declare class RuntimeClientPaths {
88
88
  identityTokenExchangePollPath(ctx: RouteContext): string;
89
89
  identityDeviceDcrPath(ctx: RouteContext): string;
90
90
  identityDeviceDcrPollPath(ctx: RouteContext): string;
91
+ identityDeviceRevokePath(ctx: RouteContext): string;
92
+ identityDeviceRevokePollPath(ctx: RouteContext): string;
93
+ identityLicenseIssuePath(ctx: RouteContext): string;
94
+ identityLicenseIssuePollPath(ctx: RouteContext): string;
91
95
  identityOpenIdSmartTokenPath(ctx: RouteContext): string;
92
96
  identityOpenIdSmartTokenPollPath(ctx: RouteContext): string;
93
97
  }
@@ -1,6 +1,6 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { GwCoreLifecycleAction } from './constants/lifecycle.js';
3
- import { buildHostRegistryPath, buildIdentityDeviceDcrPath, buildIdentityDeviceDcrPollPath, buildIdentityOpenIdSmartTokenPath, buildIdentityOpenIdSmartTokenPollPath, buildIdentityTokenExchangePath, buildIdentityTokenExchangePollPath, buildOrganizationDidBindingPath, buildOrganizationDidBindingPollPath, buildV1Path, } from './runtime-paths.js';
3
+ import { buildHostRegistryPath, buildIdentityDeviceDcrPath, buildIdentityDeviceDcrPollPath, buildIdentityDeviceRevokePath, buildIdentityDeviceRevokePollPath, buildIdentityLicenseIssuePath, buildIdentityLicenseIssuePollPath, buildIdentityOpenIdSmartTokenPath, buildIdentityOpenIdSmartTokenPollPath, buildIdentityTokenExchangePath, buildIdentityTokenExchangePollPath, buildOrganizationDidBindingPath, buildOrganizationDidBindingPollPath, buildV1Path, } from './runtime-paths.js';
4
4
  import { requireHostRouteContext, requireRouteContext, routeCtxFromInput, } from './runtime-route-context.js';
5
5
  export class RuntimeClientPaths {
6
6
  constructor(defaultCtx) {
@@ -102,6 +102,10 @@ export class RuntimeClientPaths {
102
102
  identityTokenExchangePollPath(ctx) { return buildIdentityTokenExchangePollPath(ctx); }
103
103
  identityDeviceDcrPath(ctx) { return buildIdentityDeviceDcrPath(ctx); }
104
104
  identityDeviceDcrPollPath(ctx) { return buildIdentityDeviceDcrPollPath(ctx); }
105
+ identityDeviceRevokePath(ctx) { return buildIdentityDeviceRevokePath(ctx); }
106
+ identityDeviceRevokePollPath(ctx) { return buildIdentityDeviceRevokePollPath(ctx); }
107
+ identityLicenseIssuePath(ctx) { return buildIdentityLicenseIssuePath(ctx); }
108
+ identityLicenseIssuePollPath(ctx) { return buildIdentityLicenseIssuePollPath(ctx); }
105
109
  identityOpenIdSmartTokenPath(ctx) { return buildIdentityOpenIdSmartTokenPath(ctx); }
106
110
  identityOpenIdSmartTokenPollPath(ctx) { return buildIdentityOpenIdSmartTokenPollPath(ctx); }
107
111
  }
@@ -8,5 +8,11 @@ export declare function buildIdentityTokenExchangePath(ctx: RouteContext): strin
8
8
  export declare function buildIdentityTokenExchangePollPath(ctx: RouteContext): string;
9
9
  export declare function buildIdentityDeviceDcrPath(ctx: RouteContext): string;
10
10
  export declare function buildIdentityDeviceDcrPollPath(ctx: RouteContext): string;
11
+ /** Canonical controller operation that reserves an employee seat activation credential. */
12
+ export declare function buildIdentityLicenseIssuePath(ctx: RouteContext): string;
13
+ /** Poll endpoint paired with {@link buildIdentityLicenseIssuePath}. */
14
+ export declare function buildIdentityLicenseIssuePollPath(ctx: RouteContext): string;
15
+ export declare function buildIdentityDeviceRevokePath(ctx: RouteContext): string;
16
+ export declare function buildIdentityDeviceRevokePollPath(ctx: RouteContext): string;
11
17
  export declare function buildIdentityOpenIdSmartTokenPath(ctx: RouteContext): string;
12
18
  export declare function buildIdentityOpenIdSmartTokenPollPath(ctx: RouteContext): string;
@@ -1,5 +1,6 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { buildGwCoreTenantResourceActionPath } from 'gdc-common-utils-ts/utils/gw-core-path';
3
+ import { IdentityAuthActions, IdentityAuthRouteSegments } from 'gdc-common-utils-ts/constants/identity-auth';
3
4
  export function buildV1Path(routeCtx, section, format, resourceType, action) {
4
5
  return buildGwCoreTenantResourceActionPath({
5
6
  tenantId: routeCtx.tenantId,
@@ -22,16 +23,33 @@ export function buildOrganizationDidBindingPollPath(routeCtx) {
22
23
  return `/${encodeURIComponent(routeCtx.tenantId)}/cds-${encodeURIComponent(routeCtx.jurisdiction)}/v1/${encodeURIComponent(routeCtx.sector)}/did/document/_binding-response`;
23
24
  }
24
25
  export function buildIdentityTokenExchangePath(ctx) {
25
- return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_exchange`;
26
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.Exchange);
26
27
  }
27
28
  export function buildIdentityTokenExchangePollPath(ctx) {
28
- return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_exchange-response`;
29
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.ExchangeResponse);
29
30
  }
30
31
  export function buildIdentityDeviceDcrPath(ctx) {
31
- return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_dcr`;
32
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.Dcr);
32
33
  }
33
34
  export function buildIdentityDeviceDcrPollPath(ctx) {
34
- return `/${encodeURIComponent('host')}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/identity/auth/_dcr-response`;
35
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.DcrResponse);
36
+ }
37
+ /** Canonical controller operation that reserves an employee seat activation credential. */
38
+ export function buildIdentityLicenseIssuePath(ctx) {
39
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.Issue);
40
+ }
41
+ /** Poll endpoint paired with {@link buildIdentityLicenseIssuePath}. */
42
+ export function buildIdentityLicenseIssuePollPath(ctx) {
43
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.IssueResponse);
44
+ }
45
+ export function buildIdentityDeviceRevokePath(ctx) {
46
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.Revoke);
47
+ }
48
+ export function buildIdentityDeviceRevokePollPath(ctx) {
49
+ return buildIdentityAuthPath(ctx, IdentityAuthActions.RevokeResponse);
50
+ }
51
+ function buildIdentityAuthPath(ctx, action) {
52
+ return `/${encodeURIComponent(IdentityAuthRouteSegments.Host)}/cds-${encodeURIComponent(ctx.jurisdiction)}/${IdentityAuthRouteSegments.Version}/${encodeURIComponent(ctx.sector)}/${encodeURIComponent(ctx.tenantId)}/${IdentityAuthRouteSegments.Section}/${IdentityAuthRouteSegments.Format}/${encodeURIComponent(action)}`;
35
53
  }
36
54
  export function buildIdentityOpenIdSmartTokenPath(ctx) {
37
55
  return `/${encodeURIComponent(ctx.tenantId)}/cds-${encodeURIComponent(ctx.jurisdiction)}/v1/${encodeURIComponent(ctx.sector)}/identity/openid/smart/token`;
@@ -22,6 +22,8 @@ export type ServerProfileRecord = Readonly<{
22
22
  routeContext: RouteContext;
23
23
  allowedSubjectDids: string[];
24
24
  clientId: string;
25
+ /** Stable non-secret id of the browser/app installation registered by DCR. */
26
+ clientInstanceId?: string;
25
27
  deviceDid: string;
26
28
  publicJwks: Record<string, unknown>[];
27
29
  /** Public recipient key for local confidential storage; not a DCR communication key. */
@@ -67,6 +69,8 @@ export type ServerProfileEnrollmentInput = Readonly<{
67
69
  pin: string;
68
70
  idToken: string;
69
71
  activationCode: string;
72
+ /** Explicit per-installation id. Older callers fall back to the first DCR key id. */
73
+ clientInstanceId?: string;
70
74
  vpToken: string;
71
75
  /** Registered web redirect URIs included in the OpenID DCR metadata. */
72
76
  dcrRedirectUris?: string[];
@@ -1,5 +1,6 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { randomBytes } from 'node:crypto';
3
+ import { IdentityDcrMetadataFields, IdentityDeviceInfoFields } from 'gdc-common-utils-ts/constants/identity-auth';
3
4
  import { TransportProfiles, } from 'gdc-sdk-core-ts';
4
5
  import { NodeManagedWallet } from './node-managed-wallet.js';
5
6
  import { NodeHttpClient } from './node-runtime-client.js';
@@ -26,6 +27,11 @@ export class ServerProfileSessionManager {
26
27
  const wallet = await this.createWallet(walletKeyDerivationId, seed);
27
28
  const context = walletContext(walletKeyDerivationId);
28
29
  const publicKeys = await wallet.getPublicJwks(context, {});
30
+ const clientInstanceId = String(input.clientInstanceId
31
+ || publicKeys.find((entry) => entry.purpose !== 'document-at-rest')?.kid
32
+ || '').trim();
33
+ if (!clientInstanceId)
34
+ throw new Error('Server profile enrollment requires clientInstanceId or a DCR public key id.');
29
35
  const storagePublicJwk = publicKeys.find((entry) => entry.purpose === 'document-at-rest')?.publicJwk;
30
36
  if (!storagePublicJwk)
31
37
  throw new Error('Server profile enrollment requires a document-at-rest ML-KEM key.');
@@ -35,6 +41,9 @@ export class ServerProfileSessionManager {
35
41
  activationCode: input.activationCode,
36
42
  idToken: input.idToken,
37
43
  dcrPayload: {
44
+ [IdentityDcrMetadataFields.ExtendedDeviceInfo]: {
45
+ [IdentityDeviceInfoFields.DeviceId]: clientInstanceId,
46
+ },
38
47
  application_type: 'web',
39
48
  ...(input.dcrRedirectUris?.length ? { redirect_uris: unique(input.dcrRedirectUris) } : {}),
40
49
  ...(input.dcrClientName ? { client_name: input.dcrClientName } : {}),
@@ -61,6 +70,7 @@ export class ServerProfileSessionManager {
61
70
  routeContext: input.routeContext,
62
71
  allowedSubjectDids: unique(input.allowedSubjectDids),
63
72
  clientId,
73
+ clientInstanceId,
64
74
  deviceDid,
65
75
  publicJwks: publicKeys.filter((entry) => entry.purpose !== 'document-at-rest').map((entry) => entry.publicJwk),
66
76
  storagePublicJwk: storagePublicJwk,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.4.2",
3
+ "version": "2.4.3",
4
4
  "description": "Next-generation Node runtime package for the GDC SDK family",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Antifraud Services Inc.",
@@ -37,7 +37,7 @@
37
37
  "test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
38
38
  },
39
39
  "dependencies": {
40
- "gdc-common-utils-ts": "2.5.3",
40
+ "gdc-common-utils-ts": "2.5.4",
41
41
  "gdc-sdk-core-ts": "~2.3.18"
42
42
  },
43
43
  "devDependencies": {