gdc-sdk-node-ts 2.4.33 → 2.4.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,6 +113,8 @@ If you need the shortest path:
113
113
 
114
114
  - main onboarding guide:
115
115
  [docs/101-SDK_END_TO_END.md](./docs/101-SDK_END_TO_END.md)
116
+ - wallet `context`, stable `runtimeId` and KMS-encrypted seed custody:
117
+ [docs/101-WALLET_CONTEXT_AND_KEY_CUSTODY.md](./docs/101-WALLET_CONTEXT_AND_KEY_CUSTODY.md)
116
118
  - first public actor surfaces:
117
119
  `HostOnboardingSdk`, `OrganizationControllerSdk`,
118
120
  `IndividualControllerSdk`, `ProfessionalSdk`
@@ -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,24 +77,29 @@ export declare class NodeManagedWallet implements IWallet {
67
77
  */
68
78
  getPublicJwks(context?: WalletExecutionContext, filter?: WalletKeySelection): Promise<WalletKeyDescriptor[]>;
69
79
  /**
70
- * Initializes the runtime communication wallet and returns exactly the
71
- * public signing/encryption JWKS accepted as `controller.publicKeys` by the
72
- * legacy organization activation builder.
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
- * This does not replace the professional-role signing key supplied as
75
- * `controller.publicSignKey`. The role key identifies the historical legal
76
- * representative; these runtime keys protect DIDComm communications.
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
- * used, protect `seedMaterial` in the portal wallet store (for example behind
80
- * the user's PIN and a server-held KEK) so the same private keys can be
81
- * reconstructed after restart. ICA and GW receive only this returned public
82
- * JWKS.
91
+ * used, protect `seedMaterial` in the portal wallet store (for example with
92
+ * its KMS/KEK and optionally a user PIN) and persist the same non-secret
93
+ * `context.runtime.runtimeId`. A fresh `NodeManagedWallet` reconstructs the
94
+ * same private keys from that seed and context after restart, so private JWKs
95
+ * do not need separate persistence. Only the returned public JWKS may be
96
+ * submitted when the selected high-level onboarding flow requires it.
83
97
  */
84
98
  initializeCommunicationJsonWebKeySet(context: WalletExecutionContext, options?: NodeCommunicationWalletInitialization): Promise<JwkSet>;
85
99
  /**
86
100
  * Returns only the public DIDComm signing/encryption keys for a previously
87
- * initialized runtime wallet. Private material never leaves the wallet.
101
+ * initialized user/profile device runtime wallet. Private material never
102
+ * leaves the wallet, and the returned keys do not convey an actor role.
88
103
  */
89
104
  getCommunicationJsonWebKeySet(context: WalletExecutionContext): Promise<JwkSet>;
90
105
  /**
@@ -137,6 +152,12 @@ export declare class NodeManagedWallet implements IWallet {
137
152
  * Builds one compact JWS using one managed signing key.
138
153
  */
139
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>;
140
161
  /**
141
162
  * Builds one detached compact JWS using one managed signing key.
142
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,19 +135,23 @@ export class NodeManagedWallet {
134
135
  return descriptors;
135
136
  }
136
137
  /**
137
- * Initializes the runtime communication wallet and returns exactly the
138
- * public signing/encryption JWKS accepted as `controller.publicKeys` by the
139
- * legacy organization activation builder.
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
- * This does not replace the professional-role signing key supplied as
142
- * `controller.publicSignKey`. The role key identifies the historical legal
143
- * representative; these runtime keys protect DIDComm communications.
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
- * used, protect `seedMaterial` in the portal wallet store (for example behind
147
- * the user's PIN and a server-held KEK) so the same private keys can be
148
- * reconstructed after restart. ICA and GW receive only this returned public
149
- * JWKS.
149
+ * used, protect `seedMaterial` in the portal wallet store (for example with
150
+ * its KMS/KEK and optionally a user PIN) and persist the same non-secret
151
+ * `context.runtime.runtimeId`. A fresh `NodeManagedWallet` reconstructs the
152
+ * same private keys from that seed and context after restart, so private JWKs
153
+ * do not need separate persistence. Only the returned public JWKS may be
154
+ * submitted when the selected high-level onboarding flow requires it.
150
155
  */
151
156
  async initializeCommunicationJsonWebKeySet(context, options = {}) {
152
157
  if (!context.runtime?.runtimeId) {
@@ -162,7 +167,8 @@ export class NodeManagedWallet {
162
167
  }
163
168
  /**
164
169
  * Returns only the public DIDComm signing/encryption keys for a previously
165
- * initialized runtime wallet. Private material never leaves the wallet.
170
+ * initialized user/profile device runtime wallet. Private material never
171
+ * leaves the wallet, and the returned keys do not convey an actor role.
166
172
  */
167
173
  async getCommunicationJsonWebKeySet(context) {
168
174
  if (!context.runtime?.runtimeId) {
@@ -333,6 +339,37 @@ export class NodeManagedWallet {
333
339
  const signature = await this.sign(prepared.signingInput, context, request.key);
334
340
  return buildJwtCompact(prepared.encodedHeader, prepared.encodedPayload, signature);
335
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
+ }
336
373
  /**
337
374
  * Builds one detached compact JWS using one managed signing key.
338
375
  */
@@ -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
- vpToken: input.idToken,
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
  }
@@ -0,0 +1,16 @@
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>;
@@ -0,0 +1,34 @@
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
+ }
@@ -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
- protectedVpToken: PinProtectedProfileSecret;
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
- /** Signed actor/controller VP protected with the profile for later proof and session operations. */
99
- vpToken: string;
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
- protectedVpToken: await protectServerProfileSecret(input.vpToken, input.pin, `${input.profileId}:vp-token`, this.options.sealer, this.options.profileProtection),
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 = await openServerProfileSecret(profile.protectedVpToken, input.pin, `${profile.profileId}:vp-token`, this.options.sealer);
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.33",
3
+ "version": "2.4.35",
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.",