gdc-sdk-node-ts 2.4.34 → 2.4.36
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/dist/digital-twin.d.ts +12 -0
- package/dist/digital-twin.js +37 -2
- package/dist/individual-controller-backend-runtime.d.ts +2 -2
- package/dist/individual-onboarding.d.ts +14 -1
- package/dist/individual-onboarding.js +16 -1
- package/dist/node-managed-wallet.d.ts +28 -9
- package/dist/node-managed-wallet.js +44 -9
- package/dist/node-runtime-client.d.ts +2 -2
- package/dist/orchestration/client-port.d.ts +2 -2
- package/dist/orchestration/digital-twin-sdk.d.ts +8 -1
- package/dist/orchestration/digital-twin-sdk.js +8 -1
- package/dist/orchestration/individual-controller-sdk.d.ts +2 -2
- package/dist/organization-employee-lifecycle.js +7 -1
- package/dist/server-profile-session.d.ts +47 -4
- package/dist/server-profile-session.js +82 -3
- package/package.json +2 -2
- package/dist/organization-registration-authorization.d.ts +0 -16
- package/dist/organization-registration-authorization.js +0 -34
package/dist/digital-twin.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export declare function assertDigitalTwinSubjectId(value: unknown): asserts valu
|
|
|
10
10
|
/** Search parameters added by the digital-twin Composition profile. */
|
|
11
11
|
export declare const DigitalTwinSearchParameter: Readonly<{
|
|
12
12
|
Section: "section";
|
|
13
|
+
DateFrom: "date-from";
|
|
14
|
+
DateTo: "date-to";
|
|
15
|
+
Text: "text";
|
|
13
16
|
MetaTag: "Composition.meta-tag";
|
|
14
17
|
}>;
|
|
15
18
|
export type DigitalTwinSearchInput = {
|
|
@@ -18,6 +21,15 @@ export type DigitalTwinSearchInput = {
|
|
|
18
21
|
thid?: string;
|
|
19
22
|
format?: DigitalTwinFhirFormat;
|
|
20
23
|
resourceType?: string;
|
|
24
|
+
/** One or more IPS section tokens. Basic search uses OR across sections. */
|
|
25
|
+
sections?: readonly string[];
|
|
26
|
+
/** Inclusive clinical-event lower bound as ISO date or dateTime. */
|
|
27
|
+
dateFrom?: string;
|
|
28
|
+
/** Inclusive upper bound. When omitted, GW resolves its current time. */
|
|
29
|
+
dateTo?: string;
|
|
30
|
+
/** Case- and accent-insensitive text matched against GW's private derived index. */
|
|
31
|
+
text?: string;
|
|
32
|
+
/** Advanced/compatibility filters. Do not combine with basic-search fields. */
|
|
21
33
|
filters?: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
22
34
|
pollOptions?: PollOptions;
|
|
23
35
|
};
|
package/dist/digital-twin.js
CHANGED
|
@@ -16,6 +16,9 @@ export function assertDigitalTwinSubjectId(value) {
|
|
|
16
16
|
/** Search parameters added by the digital-twin Composition profile. */
|
|
17
17
|
export const DigitalTwinSearchParameter = Object.freeze({
|
|
18
18
|
Section: 'section',
|
|
19
|
+
DateFrom: 'date-from',
|
|
20
|
+
DateTo: 'date-to',
|
|
21
|
+
Text: 'text',
|
|
19
22
|
MetaTag: 'Composition.meta-tag',
|
|
20
23
|
});
|
|
21
24
|
/**
|
|
@@ -132,10 +135,42 @@ export async function searchDigitalTwinsWithDeps(ctx, input, deps) {
|
|
|
132
135
|
const resourceType = String(input.resourceType || 'Composition').trim();
|
|
133
136
|
if (!resourceType)
|
|
134
137
|
throw new Error('Digital twin resourceType is required.');
|
|
135
|
-
const
|
|
138
|
+
const usesBasicSearch = Boolean(input.sections || input.dateFrom || input.dateTo || input.text);
|
|
139
|
+
if (usesBasicSearch && Object.keys(input.filters || {}).length > 0) {
|
|
140
|
+
throw new Error('Digital twin basic search cannot be combined with advanced filters.');
|
|
141
|
+
}
|
|
142
|
+
const sections = (input.sections || []).map((value) => String(value || '').trim()).filter(Boolean);
|
|
143
|
+
const dateFrom = String(input.dateFrom || '').trim();
|
|
144
|
+
const dateTo = String(input.dateTo || '').trim();
|
|
145
|
+
const searchText = String(input.text || '').trim();
|
|
146
|
+
if (usesBasicSearch) {
|
|
147
|
+
if (sections.length === 0)
|
|
148
|
+
throw new Error('Digital twin basic search requires at least one section.');
|
|
149
|
+
if (!dateFrom)
|
|
150
|
+
throw new Error('Digital twin basic search requires dateFrom.');
|
|
151
|
+
if (!searchText)
|
|
152
|
+
throw new Error('Digital twin basic search requires text.');
|
|
153
|
+
const fromMs = Date.parse(dateFrom);
|
|
154
|
+
const toMs = dateTo ? Date.parse(dateTo) : undefined;
|
|
155
|
+
if (Number.isNaN(fromMs))
|
|
156
|
+
throw new Error('Digital twin basic search dateFrom must be an ISO date or dateTime.');
|
|
157
|
+
if (dateTo && Number.isNaN(toMs))
|
|
158
|
+
throw new Error('Digital twin basic search dateTo must be an ISO date or dateTime.');
|
|
159
|
+
if (toMs !== undefined && toMs < fromMs)
|
|
160
|
+
throw new Error('Digital twin basic search dateTo must be on or after dateFrom.');
|
|
161
|
+
}
|
|
162
|
+
const advancedParameters = Object.entries(input.filters || {}).flatMap(([name, value]) => {
|
|
136
163
|
const values = Array.isArray(value) ? value : value === undefined ? [] : [value];
|
|
137
164
|
return values.map((item) => ({ name, valueString: String(item) }));
|
|
138
165
|
});
|
|
166
|
+
const parameters = usesBasicSearch
|
|
167
|
+
? [
|
|
168
|
+
...sections.map((section) => ({ name: DigitalTwinSearchParameter.Section, valueString: section })),
|
|
169
|
+
{ name: DigitalTwinSearchParameter.DateFrom, valueDate: dateFrom },
|
|
170
|
+
...(dateTo ? [{ name: DigitalTwinSearchParameter.DateTo, valueDate: dateTo }] : []),
|
|
171
|
+
{ name: DigitalTwinSearchParameter.Text, valueString: searchText },
|
|
172
|
+
]
|
|
173
|
+
: advancedParameters;
|
|
139
174
|
const thid = String(input.thid || randomUUID());
|
|
140
175
|
const body = format === 'org.hl7.fhir.api'
|
|
141
176
|
? {
|
|
@@ -145,7 +180,7 @@ export async function searchDigitalTwinsWithDeps(ctx, input, deps) {
|
|
|
145
180
|
meta: {
|
|
146
181
|
claims: Object.fromEntries([
|
|
147
182
|
['@context', format],
|
|
148
|
-
...parameters.map((parameter) => [parameter.name, parameter.valueString]),
|
|
183
|
+
...parameters.map((parameter) => [parameter.name, parameter.valueString || parameter.valueDate || '']),
|
|
149
184
|
]),
|
|
150
185
|
},
|
|
151
186
|
}],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SubmitAndPollResult } from 'gdc-sdk-core-ts';
|
|
2
2
|
import type { FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
3
|
-
import type { IndividualOrganizationConfirmOrderInput, RouteContext } from './individual-onboarding.js';
|
|
3
|
+
import type { IndividualOrganizationConfirmOrderInput, IndividualOrganizationOrderResult, RouteContext } from './individual-onboarding.js';
|
|
4
4
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from './individual-start.js';
|
|
5
5
|
import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizationRegistrationResult } from './family-organization-registration.js';
|
|
6
6
|
import type { FamilyOrganizationSearchInput } from './family-organization-search.js';
|
|
@@ -45,7 +45,7 @@ export declare class IndividualControllerBackendRuntime {
|
|
|
45
45
|
/**
|
|
46
46
|
* Confirms the order returned by the individual bootstrap flow.
|
|
47
47
|
*/
|
|
48
|
-
confirmIndividualOrganizationOrder(profile: BackendIndividualControllerProfile, input: IndividualOrganizationConfirmOrderInput): Promise<
|
|
48
|
+
confirmIndividualOrganizationOrder(profile: BackendIndividualControllerProfile, input: IndividualOrganizationConfirmOrderInput): Promise<IndividualOrganizationOrderResult>;
|
|
49
49
|
/**
|
|
50
50
|
* Reads the authoritative subject document through the canonical
|
|
51
51
|
* `Communication -> Subject/$summary -> FHIR Parameters` lifecycle.
|
|
@@ -21,6 +21,13 @@ export type IndividualOrganizationConfirmOrderInput = {
|
|
|
21
21
|
timeoutSeconds?: number;
|
|
22
22
|
intervalSeconds?: number;
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Terminal individual Order result with the opaque code required by the
|
|
26
|
+
* subsequent managed-wallet activation and DCR flow.
|
|
27
|
+
*/
|
|
28
|
+
export type IndividualOrganizationOrderResult = SubmitAndPollResult & Readonly<{
|
|
29
|
+
activationCode: string;
|
|
30
|
+
}>;
|
|
24
31
|
type ConfirmIndividualOrganizationOrderDeps = {
|
|
25
32
|
input: IndividualOrganizationConfirmOrderInput;
|
|
26
33
|
routeCtx: RouteContext;
|
|
@@ -32,5 +39,11 @@ type ConfirmIndividualOrganizationOrderDeps = {
|
|
|
32
39
|
thid?: string;
|
|
33
40
|
} & Record<string, unknown>, options?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
34
41
|
};
|
|
35
|
-
export declare function confirmIndividualOrganizationOrderWithDeps(deps: ConfirmIndividualOrganizationOrderDeps): Promise<
|
|
42
|
+
export declare function confirmIndividualOrganizationOrderWithDeps(deps: ConfirmIndividualOrganizationOrderDeps): Promise<IndividualOrganizationOrderResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Reads the opaque controller activation code from a completed individual
|
|
45
|
+
* Order. Integrators should consume `result.activationCode` instead of calling
|
|
46
|
+
* this reader directly; it remains public for response-adapter compatibility.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readIndividualOrganizationActivationCode(responseBody: unknown): string | undefined;
|
|
36
49
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
|
+
import { extractPrimaryClaims } from 'gdc-common-utils-ts';
|
|
2
3
|
import { resolvePollOptionsFromSeconds } from './poll-options.js';
|
|
3
4
|
export async function confirmIndividualOrganizationOrderWithDeps(deps) {
|
|
4
5
|
/**
|
|
@@ -35,7 +36,21 @@ export async function confirmIndividualOrganizationOrderWithDeps(deps) {
|
|
|
35
36
|
timeoutMs: deps.defaultTimeoutMs,
|
|
36
37
|
intervalMs: deps.defaultIntervalMs,
|
|
37
38
|
});
|
|
38
|
-
|
|
39
|
+
const order = await deps.submitAndPoll(deps.individualFamilyOrderBatchPath(deps.routeCtx), deps.individualFamilyOrderPollPath(deps.routeCtx), payload, pollOptions);
|
|
40
|
+
const activationCode = readIndividualOrganizationActivationCode(order.poll.body);
|
|
41
|
+
if (!activationCode) {
|
|
42
|
+
throw new Error('confirmIndividualOrganizationOrder failed: missing controller activation code in GW Order response.');
|
|
43
|
+
}
|
|
44
|
+
return { ...order, activationCode };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Reads the opaque controller activation code from a completed individual
|
|
48
|
+
* Order. Integrators should consume `result.activationCode` instead of calling
|
|
49
|
+
* this reader directly; it remains public for response-adapter compatibility.
|
|
50
|
+
*/
|
|
51
|
+
export function readIndividualOrganizationActivationCode(responseBody) {
|
|
52
|
+
const claims = extractPrimaryClaims(responseBody);
|
|
53
|
+
return String(claims['org.schema.IndividualProduct.serialNumber'] || '').trim() || undefined;
|
|
39
54
|
}
|
|
40
55
|
function createRuntimeUuid() {
|
|
41
56
|
const fromCrypto = globalThis.crypto?.randomUUID?.();
|
|
@@ -11,6 +11,16 @@ export type NodeManagedWalletOptions = {
|
|
|
11
11
|
resolveRecipientJwk?: (recipientDid: string) => Promise<JWK>;
|
|
12
12
|
policy?: Partial<NodeManagedWalletPolicy>;
|
|
13
13
|
};
|
|
14
|
+
export type NodeProfessionalIdentityVpInput = Readonly<{
|
|
15
|
+
clientId: string;
|
|
16
|
+
actorDid: string;
|
|
17
|
+
profileDid?: string;
|
|
18
|
+
role: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
sameAs?: string | readonly string[];
|
|
21
|
+
telephone?: string;
|
|
22
|
+
credentialMaterial?: string;
|
|
23
|
+
}>;
|
|
14
24
|
export type NodeCommunicationWalletInitialization = {
|
|
15
25
|
/**
|
|
16
26
|
* Stable secret used to reconstruct the same communication keys after a
|
|
@@ -67,26 +77,29 @@ export declare class NodeManagedWallet implements IWallet {
|
|
|
67
77
|
*/
|
|
68
78
|
getPublicJwks(context?: WalletExecutionContext, filter?: WalletKeySelection): Promise<WalletKeyDescriptor[]>;
|
|
69
79
|
/**
|
|
70
|
-
* Initializes
|
|
71
|
-
* public signing/encryption JWKS
|
|
72
|
-
*
|
|
80
|
+
* Initializes one user/profile device communication wallet and returns only
|
|
81
|
+
* its public signing/encryption JWKS. The wallet is actor-role neutral: the
|
|
82
|
+
* same custody contract can back a controller, employee/professional or
|
|
83
|
+
* individual-controller profile.
|
|
73
84
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
85
|
+
* These communication keys do not replace the actor's person/professional-
|
|
86
|
+
* role signing key. In the historical legal-representative flow, for example,
|
|
87
|
+
* this returned set is accepted as `controller.publicKeys`, while the
|
|
88
|
+
* independent role key is supplied as `controller.publicSignKey`.
|
|
77
89
|
*
|
|
78
90
|
* The caller owns durable wallet custody. When deterministic provisioning is
|
|
79
91
|
* used, protect `seedMaterial` in the portal wallet store (for example with
|
|
80
92
|
* its KMS/KEK and optionally a user PIN) and persist the same non-secret
|
|
81
93
|
* `context.runtime.runtimeId`. A fresh `NodeManagedWallet` reconstructs the
|
|
82
94
|
* same private keys from that seed and context after restart, so private JWKs
|
|
83
|
-
* do not need separate persistence.
|
|
84
|
-
*
|
|
95
|
+
* do not need separate persistence. Only the returned public JWKS may be
|
|
96
|
+
* submitted when the selected high-level onboarding flow requires it.
|
|
85
97
|
*/
|
|
86
98
|
initializeCommunicationJsonWebKeySet(context: WalletExecutionContext, options?: NodeCommunicationWalletInitialization): Promise<JwkSet>;
|
|
87
99
|
/**
|
|
88
100
|
* Returns only the public DIDComm signing/encryption keys for a previously
|
|
89
|
-
* initialized runtime wallet. Private material never
|
|
101
|
+
* initialized user/profile device runtime wallet. Private material never
|
|
102
|
+
* leaves the wallet, and the returned keys do not convey an actor role.
|
|
90
103
|
*/
|
|
91
104
|
getCommunicationJsonWebKeySet(context: WalletExecutionContext): Promise<JwkSet>;
|
|
92
105
|
/**
|
|
@@ -139,6 +152,12 @@ export declare class NodeManagedWallet implements IWallet {
|
|
|
139
152
|
* Builds one compact JWS using one managed signing key.
|
|
140
153
|
*/
|
|
141
154
|
signCompactJws(context: WalletExecutionContext, request: WalletCompactJwsRequest): Promise<string>;
|
|
155
|
+
/**
|
|
156
|
+
* Builds and signs the canonical employee/professional VP with the managed
|
|
157
|
+
* `vp-token-signing` key. Callers provide identity facts only; they do not
|
|
158
|
+
* assemble JOSE headers, choose a JWK or handle private key material.
|
|
159
|
+
*/
|
|
160
|
+
signProfessionalIdentityVp(context: WalletExecutionContext, input: NodeProfessionalIdentityVpInput): Promise<string>;
|
|
142
161
|
/**
|
|
143
162
|
* Builds one detached compact JWS using one managed signing key.
|
|
144
163
|
*/
|
|
@@ -4,6 +4,7 @@ import { CryptographyService } from 'gdc-common-utils-ts/CryptographyService';
|
|
|
4
4
|
import { Content } from 'gdc-common-utils-ts/utils/content';
|
|
5
5
|
import { createJwtSigner } from 'gdc-common-utils-ts/utils/jwt-signer';
|
|
6
6
|
import { buildJwtCompact, prepareJwtForSignature } from 'gdc-common-utils-ts/utils/jwt';
|
|
7
|
+
import { buildProfessionalIdentityVpPayload } from 'gdc-common-utils-ts/utils/professional-smart';
|
|
7
8
|
import { NodeCryptoHelper } from './node-crypto-helper.js';
|
|
8
9
|
const DEFAULT_POLICY = {
|
|
9
10
|
defaults: {
|
|
@@ -134,21 +135,23 @@ export class NodeManagedWallet {
|
|
|
134
135
|
return descriptors;
|
|
135
136
|
}
|
|
136
137
|
/**
|
|
137
|
-
* Initializes
|
|
138
|
-
* public signing/encryption JWKS
|
|
139
|
-
*
|
|
138
|
+
* Initializes one user/profile device communication wallet and returns only
|
|
139
|
+
* its public signing/encryption JWKS. The wallet is actor-role neutral: the
|
|
140
|
+
* same custody contract can back a controller, employee/professional or
|
|
141
|
+
* individual-controller profile.
|
|
140
142
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
143
|
+
* These communication keys do not replace the actor's person/professional-
|
|
144
|
+
* role signing key. In the historical legal-representative flow, for example,
|
|
145
|
+
* this returned set is accepted as `controller.publicKeys`, while the
|
|
146
|
+
* independent role key is supplied as `controller.publicSignKey`.
|
|
144
147
|
*
|
|
145
148
|
* The caller owns durable wallet custody. When deterministic provisioning is
|
|
146
149
|
* used, protect `seedMaterial` in the portal wallet store (for example with
|
|
147
150
|
* its KMS/KEK and optionally a user PIN) and persist the same non-secret
|
|
148
151
|
* `context.runtime.runtimeId`. A fresh `NodeManagedWallet` reconstructs the
|
|
149
152
|
* same private keys from that seed and context after restart, so private JWKs
|
|
150
|
-
* do not need separate persistence.
|
|
151
|
-
*
|
|
153
|
+
* do not need separate persistence. Only the returned public JWKS may be
|
|
154
|
+
* submitted when the selected high-level onboarding flow requires it.
|
|
152
155
|
*/
|
|
153
156
|
async initializeCommunicationJsonWebKeySet(context, options = {}) {
|
|
154
157
|
if (!context.runtime?.runtimeId) {
|
|
@@ -164,7 +167,8 @@ export class NodeManagedWallet {
|
|
|
164
167
|
}
|
|
165
168
|
/**
|
|
166
169
|
* Returns only the public DIDComm signing/encryption keys for a previously
|
|
167
|
-
* initialized runtime wallet. Private material never
|
|
170
|
+
* initialized user/profile device runtime wallet. Private material never
|
|
171
|
+
* leaves the wallet, and the returned keys do not convey an actor role.
|
|
168
172
|
*/
|
|
169
173
|
async getCommunicationJsonWebKeySet(context) {
|
|
170
174
|
if (!context.runtime?.runtimeId) {
|
|
@@ -335,6 +339,37 @@ export class NodeManagedWallet {
|
|
|
335
339
|
const signature = await this.sign(prepared.signingInput, context, request.key);
|
|
336
340
|
return buildJwtCompact(prepared.encodedHeader, prepared.encodedPayload, signature);
|
|
337
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Builds and signs the canonical employee/professional VP with the managed
|
|
344
|
+
* `vp-token-signing` key. Callers provide identity facts only; they do not
|
|
345
|
+
* assemble JOSE headers, choose a JWK or handle private key material.
|
|
346
|
+
*/
|
|
347
|
+
async signProfessionalIdentityVp(context, input) {
|
|
348
|
+
const [vpSigningKey] = await this.getPublicJwks(context, {
|
|
349
|
+
ownerScope: 'runtime',
|
|
350
|
+
purpose: 'vp-token-signing',
|
|
351
|
+
});
|
|
352
|
+
if (!vpSigningKey)
|
|
353
|
+
throw new Error('Managed VP signing key is not available.');
|
|
354
|
+
const payload = buildProfessionalIdentityVpPayload({
|
|
355
|
+
clientId: input.clientId,
|
|
356
|
+
actorDid: input.actorDid,
|
|
357
|
+
role: input.role,
|
|
358
|
+
...(input.email ? { email: input.email } : {}),
|
|
359
|
+
...(input.sameAs ? { sameAs: input.sameAs } : {}),
|
|
360
|
+
...(input.telephone ? { telephone: input.telephone } : {}),
|
|
361
|
+
...(input.credentialMaterial ? { credentialMaterial: input.credentialMaterial } : {}),
|
|
362
|
+
});
|
|
363
|
+
return this.signCompactJws(context, {
|
|
364
|
+
header: { typ: 'JWT', jwk: vpSigningKey.publicJwk },
|
|
365
|
+
claims: {
|
|
366
|
+
iss: vpSigningKey.kid,
|
|
367
|
+
sub: input.profileDid || input.actorDid,
|
|
368
|
+
...payload,
|
|
369
|
+
},
|
|
370
|
+
key: { ownerScope: 'runtime', purpose: 'vp-token-signing' },
|
|
371
|
+
});
|
|
372
|
+
}
|
|
338
373
|
/**
|
|
339
374
|
* Builds one detached compact JWS using one managed signing key.
|
|
340
375
|
*/
|
|
@@ -5,7 +5,7 @@ import { type ResolvedAppInfo, type CommunicationClinicalFormatRenderers, type S
|
|
|
5
5
|
import { type HostRouteContext, type HostedTenantLifecycleInput } from './host-onboarding.js';
|
|
6
6
|
import type { HostedTenantDescendantKind } from './host-onboarding.js';
|
|
7
7
|
import type { NodeLegalOrganizationVerificationTransactionInput, NodeOrganizationDidBindingInput, NodeOrganizationActivationInput } from './orchestration/client-port.js';
|
|
8
|
-
import { type IndividualOrganizationConfirmOrderInput, type RouteContext } from './individual-onboarding.js';
|
|
8
|
+
import { type IndividualOrganizationConfirmOrderInput, type IndividualOrganizationOrderResult, type RouteContext } from './individual-onboarding.js';
|
|
9
9
|
import { type EnsureFamilyOrganizationRegistrationInput } from './family-organization-registration.js';
|
|
10
10
|
import { type FamilyOrganizationSearchInput } from './family-organization-search.js';
|
|
11
11
|
import { type SmartTokenRequestInput } from './smart-token.js';
|
|
@@ -376,7 +376,7 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
|
|
|
376
376
|
/**
|
|
377
377
|
* Confirms the order returned by `startIndividualOrganization(...)`.
|
|
378
378
|
*/
|
|
379
|
-
confirmIndividualOrganizationOrder(input: IndividualOrganizationConfirmOrderInput): Promise<
|
|
379
|
+
confirmIndividualOrganizationOrder(input: IndividualOrganizationConfirmOrderInput): Promise<IndividualOrganizationOrderResult>;
|
|
380
380
|
/**
|
|
381
381
|
* Disables a hosted individual/family organization without releasing licenses.
|
|
382
382
|
*
|
|
@@ -9,7 +9,7 @@ import type { EmployeeDeviceActivationResult, EmployeeDeviceActivationRequestInp
|
|
|
9
9
|
import type { OrganizationEmployeeProvisioningInput, OrganizationEmployeeProvisioningResult } from '../organization-employee-lifecycle.js';
|
|
10
10
|
import type { OrganizationEmployeeLifecycleRecord } from 'gdc-common-utils-ts/models/organization-employee-lifecycle';
|
|
11
11
|
import type { HostRouteContext, HostedTenantLifecycleInput, LegalOrganizationOrderInput } from '../host-onboarding.js';
|
|
12
|
-
import type { IndividualOrganizationConfirmOrderInput, RouteContext } from '../individual-onboarding.js';
|
|
12
|
+
import type { IndividualOrganizationConfirmOrderInput, IndividualOrganizationOrderResult, RouteContext } from '../individual-onboarding.js';
|
|
13
13
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from '../individual-start.js';
|
|
14
14
|
import type { FamilyOrganizationSearchInput } from '../family-organization-search.js';
|
|
15
15
|
import type { FhirR5SubscriptionTopic } from 'gdc-common-utils-ts/models/fhir-r5-subscription';
|
|
@@ -106,7 +106,7 @@ export type RuntimeClient = {
|
|
|
106
106
|
startIndividualOrganization?: (input: IndividualOrganizationBootstrapInput) => Promise<IndividualOrganizationStartResult>;
|
|
107
107
|
searchFamilyOrganization?: (ctx: RouteContext, input: FamilyOrganizationSearchInput) => Promise<FamilyOrganizationSummary | null>;
|
|
108
108
|
ensureFamilyOrganizationRegistration?: (ctx: RouteContext, input: EnsureFamilyOrganizationRegistrationInput) => Promise<EnsureFamilyOrganizationRegistrationResult>;
|
|
109
|
-
confirmIndividualOrganizationOrder?: (input: IndividualOrganizationConfirmOrderInput) => Promise<
|
|
109
|
+
confirmIndividualOrganizationOrder?: (input: IndividualOrganizationConfirmOrderInput) => Promise<IndividualOrganizationOrderResult>;
|
|
110
110
|
disableIndividual?: (ctx: RouteContext, input: IndividualOrganizationLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
111
111
|
purgeIndividual?: (ctx: RouteContext, input: IndividualOrganizationLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
112
112
|
disableIndividualMember?: (ctx: RouteContext, input: IndividualMemberLifecycleInput, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
@@ -10,7 +10,14 @@ export declare class DigitalTwinSdk {
|
|
|
10
10
|
constructor(client: NodeRuntimeClient, actorDid?: string);
|
|
11
11
|
/** Requests the SMART token used by subsequent digital-twin operations. */
|
|
12
12
|
requestSmartToken(input: SmartTokenRequestInput): Promise<SmartTokenExchangeResult>;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Searches pseudonymous records and exposes matched Compositions directly.
|
|
15
|
+
*
|
|
16
|
+
* MVP basic search supplies `sections`, `dateFrom`, optional `dateTo`, and
|
|
17
|
+
* `text`. GW resolves an omitted `dateTo` to its current time, applies OR
|
|
18
|
+
* across sections and requires text/date to match the same clinical record.
|
|
19
|
+
* Organization/tenant scope comes from the authenticated SMART token.
|
|
20
|
+
*/
|
|
14
21
|
search(ctx: RouteContext, input: DigitalTwinSearchInput): Promise<DigitalTwinSearchResult>;
|
|
15
22
|
/**
|
|
16
23
|
* Saves a tagged researcher-owned working selection for one matching twin.
|
|
@@ -28,7 +28,14 @@ export class DigitalTwinSdk {
|
|
|
28
28
|
this.researcherDid = actorDid;
|
|
29
29
|
return result;
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Searches pseudonymous records and exposes matched Compositions directly.
|
|
33
|
+
*
|
|
34
|
+
* MVP basic search supplies `sections`, `dateFrom`, optional `dateTo`, and
|
|
35
|
+
* `text`. GW resolves an omitted `dateTo` to its current time, applies OR
|
|
36
|
+
* across sections and requires text/date to match the same clinical record.
|
|
37
|
+
* Organization/tenant scope comes from the authenticated SMART token.
|
|
38
|
+
*/
|
|
32
39
|
async search(ctx, input) {
|
|
33
40
|
const filters = { ...(input.filters || {}) };
|
|
34
41
|
const isPrivateSelectionSearch = Object.keys(filters).some((name) => {
|
|
@@ -3,7 +3,7 @@ import { type NodeRuntimeClient, type PollOptions, type SubmitAndPollResult, typ
|
|
|
3
3
|
import type { FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
|
|
4
4
|
import type { EnsureFamilyOrganizationRegistrationInput, EnsureFamilyOrganizationRegistrationResult } from '../family-organization-registration.js';
|
|
5
5
|
import type { FamilyOrganizationSearchInput } from '../family-organization-search.js';
|
|
6
|
-
import type { IndividualOrganizationConfirmOrderInput, RouteContext } from '../individual-onboarding.js';
|
|
6
|
+
import type { IndividualOrganizationConfirmOrderInput, IndividualOrganizationOrderResult, RouteContext } from '../individual-onboarding.js';
|
|
7
7
|
import type { IndividualOrganizationBootstrapInput, IndividualOrganizationStartResult } from '../individual-start.js';
|
|
8
8
|
import type { NodeCapability } from '../session.js';
|
|
9
9
|
import type { IndividualOrganizationLifecycleInput } from 'gdc-sdk-core-ts';
|
|
@@ -39,7 +39,7 @@ export declare class IndividualControllerSdk {
|
|
|
39
39
|
/**
|
|
40
40
|
* Confirms the order returned by `startIndividualOrganization(...)`.
|
|
41
41
|
*/
|
|
42
|
-
confirmIndividualOrganizationOrder(input: IndividualOrganizationConfirmOrderInput): Promise<
|
|
42
|
+
confirmIndividualOrganizationOrder(input: IndividualOrganizationConfirmOrderInput): Promise<IndividualOrganizationOrderResult>;
|
|
43
43
|
/**
|
|
44
44
|
* Disables the hosted individual/family organization without freeing licenses.
|
|
45
45
|
*/
|
|
@@ -218,6 +218,12 @@ export async function enrollInvitedOrganizationEmployeeWithDeps(input, deps) {
|
|
|
218
218
|
clientInstanceId: input.clientInstanceId,
|
|
219
219
|
dcrRedirectUris: input.dcrRedirectUris,
|
|
220
220
|
dcrClientName: input.dcrClientName,
|
|
221
|
-
|
|
221
|
+
// The grant contains the server-verified stable actor alias and role. The
|
|
222
|
+
// profile runtime signs the VP with its managed DCR wallet after GW has
|
|
223
|
+
// returned the real client_id; idToken remains only the OIDC account proof.
|
|
224
|
+
professionalProof: {
|
|
225
|
+
role: input.grant.employeeRoleCode,
|
|
226
|
+
sameAs: input.grant.employeeActorIdentifier,
|
|
227
|
+
},
|
|
222
228
|
});
|
|
223
229
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { JWK } from 'gdc-common-utils-ts/models/jwk';
|
|
2
2
|
import type { ActorKind } from 'gdc-common-utils-ts/models/actor-session';
|
|
3
3
|
import type { LegalOrganizationVerificationTransactionInput } from 'gdc-common-utils-ts/utils/legal-organization-verification-transaction';
|
|
4
|
-
import { type ConfidentialStorageProfile, type PollOptions, type SubmitAndPollResult } from 'gdc-sdk-core-ts';
|
|
4
|
+
import { type AppInfo, type ConfidentialStorageProfile, type PollOptions, type SubmitAndPollResult } from 'gdc-sdk-core-ts';
|
|
5
5
|
import type { RouteContext } from './individual-onboarding.js';
|
|
6
6
|
import type { HostRouteContext } from './host-onboarding.js';
|
|
7
7
|
import type { SecureDidcommTransportAdapter } from 'gdc-sdk-core-ts';
|
|
@@ -31,7 +31,8 @@ export type ServerProfileRecord = Readonly<{
|
|
|
31
31
|
/** Server-owned policy; browser input is never authoritative for this value. */
|
|
32
32
|
confidentialStorageProfile?: ConfidentialStorageProfile;
|
|
33
33
|
protectedWalletSeed: PinProtectedProfileSecret;
|
|
34
|
-
|
|
34
|
+
/** Present only when this actor kind requires an independent signed role/relationship VP. */
|
|
35
|
+
protectedVpToken?: PinProtectedProfileSecret;
|
|
35
36
|
failedUnlocks: number;
|
|
36
37
|
lockedUntil?: string;
|
|
37
38
|
createdAt: string;
|
|
@@ -95,8 +96,25 @@ export type ServerProfileEnrollmentInput = Readonly<{
|
|
|
95
96
|
* derives the identity from the wallet key it creates for this profile.
|
|
96
97
|
*/
|
|
97
98
|
clientInstanceId?: string;
|
|
98
|
-
/**
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Signed actor/controller VP protected for later SMART operations. Required
|
|
101
|
+
* for organization/professional/member actors. Individual-controller
|
|
102
|
+
* profiles omit it: their account `id_token`, DCR device binding and
|
|
103
|
+
* provider-side relationship policy are evaluated independently.
|
|
104
|
+
*/
|
|
105
|
+
vpToken?: string;
|
|
106
|
+
/**
|
|
107
|
+
* High-level employee/professional proof source. When supplied, the SDK
|
|
108
|
+
* builds and signs the VP with the managed DCR wallet after registration;
|
|
109
|
+
* callers do not compose JWT/JWK material.
|
|
110
|
+
*/
|
|
111
|
+
professionalProof?: Readonly<{
|
|
112
|
+
role: string;
|
|
113
|
+
email?: string;
|
|
114
|
+
sameAs?: string | readonly string[];
|
|
115
|
+
telephone?: string;
|
|
116
|
+
credentialMaterial?: string;
|
|
117
|
+
}>;
|
|
100
118
|
/** Redirect URIs owned by this portal/app installation. */
|
|
101
119
|
redirectUris?: string[];
|
|
102
120
|
/** Human-readable portal/app name. */
|
|
@@ -132,6 +150,20 @@ export type ServerProfileOrganizationIssueInput = Readonly<{
|
|
|
132
150
|
verificationInput: LegalOrganizationVerificationTransactionInput;
|
|
133
151
|
pollOptions?: PollOptions;
|
|
134
152
|
}>;
|
|
153
|
+
export type ServerOrganizationControllerVpInput = Readonly<{
|
|
154
|
+
/** Same protected seed that committed the controller public key to ICA. */
|
|
155
|
+
walletSeed: string;
|
|
156
|
+
/** Stable application-owned derivation id reused after every restart. */
|
|
157
|
+
walletKeyDerivationId: string;
|
|
158
|
+
/** Terminal ICA/GW verification body containing the issued organization, representative and controller VCs. */
|
|
159
|
+
verificationResponseBody: unknown;
|
|
160
|
+
/** Exact organization tenant identifier represented by the controller proof. */
|
|
161
|
+
tenantId: string;
|
|
162
|
+
/** Exact audience required by the receiving GW/host policy. */
|
|
163
|
+
audience: string;
|
|
164
|
+
/** Optional stable sameAs selector when the response contains several controller credentials. */
|
|
165
|
+
controllerSameAs?: string;
|
|
166
|
+
}>;
|
|
135
167
|
/** One explicit unlock request; scopes and subject remain session-bound. */
|
|
136
168
|
export type ServerProfileUnlockInput = Readonly<{
|
|
137
169
|
ownerId: string;
|
|
@@ -166,6 +198,8 @@ export type ServerProfileSessionManagerOptions = Readonly<{
|
|
|
166
198
|
sealer: ServerProfileSealer;
|
|
167
199
|
gatewayBaseUrl: string;
|
|
168
200
|
resolveRecipientJwk: (recipientDid: string) => Promise<JWK>;
|
|
201
|
+
/** Stable identity of the portal/application, shared by all of its user wallets. */
|
|
202
|
+
appInfo?: AppInfo;
|
|
169
203
|
fetchImpl?: typeof fetch;
|
|
170
204
|
sessionTtlSeconds?: number;
|
|
171
205
|
maxFailedUnlocks?: number;
|
|
@@ -196,6 +230,15 @@ export declare class ServerProfileSessionManager {
|
|
|
196
230
|
walletSeed: string;
|
|
197
231
|
walletKeyDerivationId: string;
|
|
198
232
|
}>): Promise<ServerProfileEnrollmentPublicKey[]>;
|
|
233
|
+
/**
|
|
234
|
+
* Builds and signs the canonical controller VP directly from ICA-issued
|
|
235
|
+
* credentials. The caller never assembles a VP/JWS or exports a private key.
|
|
236
|
+
*
|
|
237
|
+
* This proof is independent from the signed OIDC id_token: the VP proves
|
|
238
|
+
* organization/controller authority, while id_token proves control of the
|
|
239
|
+
* verified login identifier used by Token/_exchange and DCR.
|
|
240
|
+
*/
|
|
241
|
+
buildOrganizationControllerVpFromIcaProof(input: ServerOrganizationControllerVpInput): Promise<string>;
|
|
199
242
|
/**
|
|
200
243
|
* Reissues an existing organization controller activation through protected
|
|
201
244
|
* DIDComm transport before a durable profile exists. The deterministic seed
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { ActorKinds } from 'gdc-common-utils-ts/constants/actor-session';
|
|
4
|
+
import { readLegalOrganizationVerificationCredentialPairFromResponseBody, readServiceControllerCredentialFromResponseBody, } from 'gdc-common-utils-ts/utils/legal-organization-verification-result';
|
|
5
|
+
import { addLegalRepresentativeCredential, addOrganizationCredential, addServiceControllerCredential, createVP, } from 'gdc-common-utils-ts/utils/vp-token';
|
|
4
6
|
import { TransportProfiles, } from 'gdc-sdk-core-ts';
|
|
5
7
|
import { NodeManagedWallet } from './node-managed-wallet.js';
|
|
6
8
|
import { NodeHttpClient } from './node-runtime-client.js';
|
|
@@ -62,6 +64,14 @@ export class ServerProfileSessionManager {
|
|
|
62
64
|
throw new Error('GW DCR did not return client_id.');
|
|
63
65
|
const deviceDid = findText(dcrBody, ['device_did', 'deviceDid', 'did']) || clientId;
|
|
64
66
|
const now = this.now();
|
|
67
|
+
const managedVpToken = input.vpToken || (input.professionalProof
|
|
68
|
+
? await buildManagedProfessionalVp(wallet, context, {
|
|
69
|
+
clientId,
|
|
70
|
+
actorDid: input.actorDid,
|
|
71
|
+
profileDid: input.profileDid,
|
|
72
|
+
...input.professionalProof,
|
|
73
|
+
})
|
|
74
|
+
: undefined);
|
|
65
75
|
const record = {
|
|
66
76
|
profileId: input.profileId,
|
|
67
77
|
walletKeyDerivationId,
|
|
@@ -80,7 +90,9 @@ export class ServerProfileSessionManager {
|
|
|
80
90
|
storagePublicJwk: storagePublicJwk,
|
|
81
91
|
confidentialStorageProfile: this.options.requiredConfidentialStorageProfile ?? 'confidential-basic-v1',
|
|
82
92
|
protectedWalletSeed: await protectServerProfileSecret(seed, input.pin, `${input.profileId}:wallet-seed`, this.options.sealer, this.options.profileProtection),
|
|
83
|
-
|
|
93
|
+
...(managedVpToken ? {
|
|
94
|
+
protectedVpToken: await protectServerProfileSecret(managedVpToken, input.pin, `${input.profileId}:vp-token`, this.options.sealer, this.options.profileProtection),
|
|
95
|
+
} : {}),
|
|
84
96
|
failedUnlocks: 0,
|
|
85
97
|
createdAt: now.toISOString(),
|
|
86
98
|
updatedAt: now.toISOString(),
|
|
@@ -109,6 +121,57 @@ export class ServerProfileSessionManager {
|
|
|
109
121
|
publicJwk: entry.publicJwk,
|
|
110
122
|
}));
|
|
111
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Builds and signs the canonical controller VP directly from ICA-issued
|
|
126
|
+
* credentials. The caller never assembles a VP/JWS or exports a private key.
|
|
127
|
+
*
|
|
128
|
+
* This proof is independent from the signed OIDC id_token: the VP proves
|
|
129
|
+
* organization/controller authority, while id_token proves control of the
|
|
130
|
+
* verified login identifier used by Token/_exchange and DCR.
|
|
131
|
+
*/
|
|
132
|
+
async buildOrganizationControllerVpFromIcaProof(input) {
|
|
133
|
+
requireBase64UrlSeed32(input.walletSeed);
|
|
134
|
+
const walletKeyDerivationId = normalizedWalletKeyDerivationId(input.walletKeyDerivationId, '');
|
|
135
|
+
if (!walletKeyDerivationId) {
|
|
136
|
+
throw new Error('buildOrganizationControllerVpFromIcaProof requires walletKeyDerivationId.');
|
|
137
|
+
}
|
|
138
|
+
const tenantId = String(input.tenantId || '').trim();
|
|
139
|
+
const audience = String(input.audience || '').trim();
|
|
140
|
+
if (!tenantId)
|
|
141
|
+
throw new Error('buildOrganizationControllerVpFromIcaProof requires tenantId.');
|
|
142
|
+
if (!audience)
|
|
143
|
+
throw new Error('buildOrganizationControllerVpFromIcaProof requires audience.');
|
|
144
|
+
const { organizationCredential, legalRepresentativeCredential } = readLegalOrganizationVerificationCredentialPairFromResponseBody(input.verificationResponseBody);
|
|
145
|
+
const controllerCredential = readServiceControllerCredentialFromResponseBody(input.verificationResponseBody, input.controllerSameAs);
|
|
146
|
+
if (!controllerCredential) {
|
|
147
|
+
throw new Error('ICA verification response does not contain a ServiceControllerCredential.');
|
|
148
|
+
}
|
|
149
|
+
const wallet = await this.createWallet(walletKeyDerivationId, input.walletSeed);
|
|
150
|
+
const context = walletContext(walletKeyDerivationId);
|
|
151
|
+
const [actorSigningKey] = await wallet.getPublicJwks(context, {
|
|
152
|
+
ownerScope: 'profile',
|
|
153
|
+
purpose: 'actor-signing',
|
|
154
|
+
});
|
|
155
|
+
if (!actorSigningKey)
|
|
156
|
+
throw new Error('Controller actor-signing key is not available.');
|
|
157
|
+
const vp = createVP({
|
|
158
|
+
iss: actorSigningKey.kid,
|
|
159
|
+
sub: tenantId,
|
|
160
|
+
aud: audience,
|
|
161
|
+
vp: { holder: actorSigningKey.kid, verifiableCredential: [] },
|
|
162
|
+
});
|
|
163
|
+
addOrganizationCredential(vp, organizationCredential);
|
|
164
|
+
addLegalRepresentativeCredential(vp, legalRepresentativeCredential);
|
|
165
|
+
addServiceControllerCredential(vp, controllerCredential);
|
|
166
|
+
return wallet.signCompactJws(context, {
|
|
167
|
+
header: {
|
|
168
|
+
typ: 'JWT',
|
|
169
|
+
jwk: actorSigningKey.publicJwk,
|
|
170
|
+
},
|
|
171
|
+
claims: vp,
|
|
172
|
+
key: { ownerScope: 'profile', purpose: 'actor-signing' },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
112
175
|
/**
|
|
113
176
|
* Reissues an existing organization controller activation through protected
|
|
114
177
|
* DIDComm transport before a durable profile exists. The deterministic seed
|
|
@@ -126,6 +189,7 @@ export class ServerProfileSessionManager {
|
|
|
126
189
|
baseUrl: this.options.gatewayBaseUrl,
|
|
127
190
|
ctx: input.routeContext,
|
|
128
191
|
bearerToken: input.bearerToken,
|
|
192
|
+
appInfo: this.options.appInfo,
|
|
129
193
|
fetchImpl: this.options.fetchImpl,
|
|
130
194
|
transportProfile: TransportProfiles.DidcommEncryptedForm,
|
|
131
195
|
secureTransportAdapter: {
|
|
@@ -153,7 +217,9 @@ export class ServerProfileSessionManager {
|
|
|
153
217
|
let vpToken;
|
|
154
218
|
try {
|
|
155
219
|
seed = await openServerProfileSecret(profile.protectedWalletSeed, input.pin, `${profile.profileId}:wallet-seed`, this.options.sealer);
|
|
156
|
-
vpToken =
|
|
220
|
+
vpToken = profile.protectedVpToken
|
|
221
|
+
? await openServerProfileSecret(profile.protectedVpToken, input.pin, `${profile.profileId}:vp-token`, this.options.sealer)
|
|
222
|
+
: undefined;
|
|
157
223
|
}
|
|
158
224
|
catch (reason) {
|
|
159
225
|
if (!(reason instanceof ProfilePinRejectedError))
|
|
@@ -183,6 +249,7 @@ export class ServerProfileSessionManager {
|
|
|
183
249
|
audience: smartTokenEndpoint,
|
|
184
250
|
idToken: input.idToken,
|
|
185
251
|
vpToken,
|
|
252
|
+
vpTokenFallback: vpToken ? undefined : 'omit',
|
|
186
253
|
clientAssertion: assertion,
|
|
187
254
|
clientAssertionType: 'private_key_jwt',
|
|
188
255
|
smartTokenKind: 'openid-smart',
|
|
@@ -247,6 +314,7 @@ export class ServerProfileSessionManager {
|
|
|
247
314
|
ctx,
|
|
248
315
|
bearerToken,
|
|
249
316
|
fetchImpl: this.options.fetchImpl,
|
|
317
|
+
appInfo: this.options.appInfo,
|
|
250
318
|
});
|
|
251
319
|
}
|
|
252
320
|
async createWallet(walletKeyDerivationId, seed) {
|
|
@@ -323,6 +391,10 @@ function profileSmartAcrValues(actorKind) {
|
|
|
323
391
|
? 'urn:antifraud:acr:openid4vp:individual'
|
|
324
392
|
: 'urn:antifraud:acr:openid4vp:employee';
|
|
325
393
|
}
|
|
394
|
+
/** Creates the role VP after DCR has returned the wallet's actual client_id. */
|
|
395
|
+
async function buildManagedProfessionalVp(wallet, context, input) {
|
|
396
|
+
return wallet.signProfessionalIdentityVp(context, input);
|
|
397
|
+
}
|
|
326
398
|
async function buildWalletClientAssertion(wallet, profile, audience, now) {
|
|
327
399
|
const seconds = Math.floor(now.getTime() / 1000);
|
|
328
400
|
return wallet.signCompactJws(walletContext(profile.walletKeyDerivationId || profile.profileId), {
|
|
@@ -361,10 +433,17 @@ function requireEnrollment(input) {
|
|
|
361
433
|
providerDid: input.providerDid,
|
|
362
434
|
activationCode: input.activationCode,
|
|
363
435
|
idToken: input.idToken,
|
|
364
|
-
vpToken: input.vpToken,
|
|
365
436
|
}))
|
|
366
437
|
if (!String(value || '').trim())
|
|
367
438
|
throw new Error(`Profile enrollment requires ${name}.`);
|
|
439
|
+
if (input.actorKind !== ActorKinds.IndividualController
|
|
440
|
+
&& !String(input.vpToken || '').trim()
|
|
441
|
+
&& !input.professionalProof) {
|
|
442
|
+
throw new Error('Profile enrollment requires vpToken for this actor kind.');
|
|
443
|
+
}
|
|
444
|
+
if (input.professionalProof && !String(input.professionalProof.role || '').trim()) {
|
|
445
|
+
throw new Error('Profile enrollment professionalProof requires role.');
|
|
446
|
+
}
|
|
368
447
|
if (!input.allowedSubjectDids.length)
|
|
369
448
|
throw new Error('Profile enrollment requires an allowed subject.');
|
|
370
449
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-sdk-node-ts",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.36",
|
|
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.",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"gdc-common-utils-ts": "2.5.
|
|
41
|
+
"gdc-common-utils-ts": "2.5.20",
|
|
42
42
|
"gdc-sdk-core-ts": "~2.3.18"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { VerifiableCredentialV2 } from 'gdc-common-utils-ts/models/verifiable-credential';
|
|
2
|
-
import type { IWallet, WalletExecutionContext, WalletKeySelection } from 'gdc-sdk-core-ts';
|
|
3
|
-
/** Input required to add one human-authorizer proof to the out-of-band VC. */
|
|
4
|
-
export type SignOrganizationRegistrationAuthorizationInput = Readonly<{
|
|
5
|
-
credential: VerifiableCredentialV2;
|
|
6
|
-
wallet: IWallet;
|
|
7
|
-
context: WalletExecutionContext;
|
|
8
|
-
key: WalletKeySelection;
|
|
9
|
-
verificationMethod: string;
|
|
10
|
-
createdAt?: string;
|
|
11
|
-
}>;
|
|
12
|
-
/**
|
|
13
|
-
* Adds one detached JWS proof made by the already-unlocked professional
|
|
14
|
-
* profile. The browser never receives the private key or clear wallet seed.
|
|
15
|
-
*/
|
|
16
|
-
export declare function signOrganizationRegistrationAuthorizationCredential(input: SignOrganizationRegistrationAuthorizationInput): Promise<VerifiableCredentialV2>;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { canonicalizeOrganizationRegistrationAuthorizationCredential, } from 'gdc-common-utils-ts/utils/organization-registration-authorization';
|
|
2
|
-
/**
|
|
3
|
-
* Adds one detached JWS proof made by the already-unlocked professional
|
|
4
|
-
* profile. The browser never receives the private key or clear wallet seed.
|
|
5
|
-
*/
|
|
6
|
-
export async function signOrganizationRegistrationAuthorizationCredential(input) {
|
|
7
|
-
if (!input.wallet.signDetachedJws) {
|
|
8
|
-
throw new Error('Wallet does not support detached organization authorization proofs.');
|
|
9
|
-
}
|
|
10
|
-
const verificationMethod = String(input.verificationMethod || '').trim();
|
|
11
|
-
if (!verificationMethod)
|
|
12
|
-
throw new Error('Organization authorization proof requires verificationMethod.');
|
|
13
|
-
const payload = canonicalizeOrganizationRegistrationAuthorizationCredential(input.credential);
|
|
14
|
-
const jws = await input.wallet.signDetachedJws(input.context, {
|
|
15
|
-
payload,
|
|
16
|
-
header: { typ: 'application/vc+ld+json' },
|
|
17
|
-
key: input.key,
|
|
18
|
-
});
|
|
19
|
-
const existingProofs = Array.isArray(input.credential.proof)
|
|
20
|
-
? input.credential.proof
|
|
21
|
-
: input.credential.proof
|
|
22
|
-
? [input.credential.proof]
|
|
23
|
-
: [];
|
|
24
|
-
return {
|
|
25
|
-
...input.credential,
|
|
26
|
-
proof: [...existingProofs, {
|
|
27
|
-
type: 'JsonWebSignature2020',
|
|
28
|
-
created: input.createdAt || new Date().toISOString(),
|
|
29
|
-
proofPurpose: 'contractAgreement',
|
|
30
|
-
verificationMethod,
|
|
31
|
-
jws,
|
|
32
|
-
}],
|
|
33
|
-
};
|
|
34
|
-
}
|