gdc-sdk-node-ts 2.4.41 → 2.4.42

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.
@@ -3,6 +3,10 @@ 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
4
  import { type AppInfo, type ConfidentialStorageProfile, type PollOptions, type SubmitAndPollResult } from 'gdc-sdk-core-ts';
5
5
  import { OrganizationControllerSdk } from './orchestration/organization-controller-sdk.js';
6
+ import { ProfessionalSdk } from './orchestration/professional-sdk.js';
7
+ import { DigitalTwinSdk } from './orchestration/digital-twin-sdk.js';
8
+ import type { SmartTokenExchangeResult } from './smart-token.js';
9
+ import type { DigitalTwinSearchInput, DigitalTwinSearchResult } from './digital-twin.js';
6
10
  import type { RouteContext } from './individual-onboarding.js';
7
11
  import type { HostRouteContext } from './host-onboarding.js';
8
12
  import type { SecureDidcommTransportAdapter } from 'gdc-sdk-core-ts';
@@ -228,6 +232,47 @@ export type OpenedServerOrganizationController = Readonly<{
228
232
  profile: ServerProfileRecord;
229
233
  sdk: OrganizationControllerSdk;
230
234
  }>;
235
+ /** Server-owned role evidence used to sign a fresh professional VP. */
236
+ export type ServerProfessionalProofInput = Readonly<{
237
+ role: string;
238
+ email?: string;
239
+ sameAs?: string | readonly string[];
240
+ telephone?: string;
241
+ credentialMaterial?: string;
242
+ }>;
243
+ /** Reopens one enrolled employee/professional profile behind the BFF boundary. */
244
+ export type ServerProfessionalOpenInput = Readonly<{
245
+ ownerId: string;
246
+ profileId: string;
247
+ /** Fresh signed OIDC id_token proving the authenticated account/email. */
248
+ idToken: string;
249
+ /** Role evidence signed by the managed DCR wallet, independently of idToken. */
250
+ professionalProof: ServerProfessionalProofInput;
251
+ /** Manager-owned PIN path; mutually exclusive with authorizedWalletSeed. */
252
+ pin?: string;
253
+ /** Server-only seed already authorized by a product passkey/session policy. */
254
+ authorizedWalletSeed?: string;
255
+ }>;
256
+ /** Business authorization requested from SMART; OpenID/JWT fields stay SDK-owned. */
257
+ export type ServerProfessionalSmartTokenInput = Readonly<{
258
+ subjectDid?: string;
259
+ purpose?: string;
260
+ scopes: string[];
261
+ acrValues?: string;
262
+ requestBodyClaims?: Record<string, unknown>;
263
+ tokenCacheKey?: string;
264
+ timeoutSeconds?: number;
265
+ intervalSeconds?: number;
266
+ }>;
267
+ /** Role-scoped professional facades with managed SMART proof methods. */
268
+ export type OpenedServerProfessional = Readonly<{
269
+ profile: ServerProfileRecord;
270
+ sdk: ProfessionalSdk;
271
+ digitalTwin: DigitalTwinSdk;
272
+ requestSmartToken(input: ServerProfessionalSmartTokenInput): Promise<SmartTokenExchangeResult>;
273
+ requestDigitalTwinSmartToken(input: ServerProfessionalSmartTokenInput): Promise<SmartTokenExchangeResult>;
274
+ searchDigitalTwins(input: Omit<DigitalTwinSearchInput, 'accessToken'>): Promise<DigitalTwinSearchResult>;
275
+ }>;
231
276
  export type ServerProfileSessionManagerOptions = Readonly<{
232
277
  store: ServerProfileStore;
233
278
  sealer: ServerProfileSealer;
@@ -307,6 +352,16 @@ export declare class ServerProfileSessionManager {
307
352
  * registered for that profile before any gateway request can be sent.
308
353
  */
309
354
  openOrganizationController(input: ServerOrganizationControllerOpenInput): Promise<OpenedServerOrganizationController>;
355
+ /**
356
+ * Opens one registered organization employee/professional without exposing
357
+ * wallet reconstruction, private_key_jwt, VP or transport plumbing.
358
+ *
359
+ * The product authorizes the PIN/passkey/session and supplies business role
360
+ * evidence. The SDK revalidates the registered key set, signs a fresh role
361
+ * VP independently from the OIDC account proof, derives the SMART endpoint
362
+ * from the durable profile route and owns the client assertion.
363
+ */
364
+ openProfessional(input: ServerProfessionalOpenInput): Promise<OpenedServerProfessional>;
310
365
  lock(ownerId: string, sessionId: string): Promise<void>;
311
366
  private createClient;
312
367
  private createWallet;
@@ -7,6 +7,8 @@ import { TransportProfiles, } from 'gdc-sdk-core-ts';
7
7
  import { NodeManagedWallet } from './node-managed-wallet.js';
8
8
  import { NodeHttpClient } from './node-runtime-client.js';
9
9
  import { OrganizationControllerSdk } from './orchestration/organization-controller-sdk.js';
10
+ import { ProfessionalSdk } from './orchestration/professional-sdk.js';
11
+ import { DigitalTwinSdk } from './orchestration/digital-twin-sdk.js';
10
12
  import { buildIdentityOpenIdSmartTokenPath } from './runtime-paths.js';
11
13
  import { createProfileDeviceActivationRequest } from './device-activation.js';
12
14
  import { ProfilePinRejectedError, openServerProfileSecret, protectServerProfileSecret, } from './server-profile-protection.js';
@@ -453,6 +455,115 @@ export class ServerProfileSessionManager {
453
455
  });
454
456
  return { profile, sdk: new OrganizationControllerSdk(client) };
455
457
  }
458
+ /**
459
+ * Opens one registered organization employee/professional without exposing
460
+ * wallet reconstruction, private_key_jwt, VP or transport plumbing.
461
+ *
462
+ * The product authorizes the PIN/passkey/session and supplies business role
463
+ * evidence. The SDK revalidates the registered key set, signs a fresh role
464
+ * VP independently from the OIDC account proof, derives the SMART endpoint
465
+ * from the durable profile route and owns the client assertion.
466
+ */
467
+ async openProfessional(input) {
468
+ const profile = await this.requireOwnedProfile(input.ownerId, input.profileId);
469
+ const isProfessional = profile.actorKind === ActorKinds.OrganizationEmployee
470
+ || profile.actorKind === ActorKinds.Professional;
471
+ if (!isProfessional || profile.actorMode !== 'member') {
472
+ throw new Error('Profile is not an organization employee or professional.');
473
+ }
474
+ const idToken = String(input.idToken || '').trim();
475
+ const role = String(input.professionalProof?.role || '').trim();
476
+ if (!idToken)
477
+ throw new Error('Opening a professional requires a signed OIDC idToken.');
478
+ if (!role)
479
+ throw new Error('Opening a professional requires role proof.');
480
+ const hasPin = Boolean(String(input.pin || '').trim());
481
+ const hasAuthorizedSeed = Boolean(String(input.authorizedWalletSeed || '').trim());
482
+ if (hasPin === hasAuthorizedSeed) {
483
+ throw new Error('Opening a professional requires exactly one of pin or authorizedWalletSeed.');
484
+ }
485
+ const seed = hasAuthorizedSeed
486
+ ? String(input.authorizedWalletSeed)
487
+ : await openServerProfileSecret(profile.protectedWalletSeed, String(input.pin), `${profile.profileId}:wallet-seed`, this.options.sealer);
488
+ requireBase64UrlSeed32(seed);
489
+ const walletKeyDerivationId = normalizedWalletKeyDerivationId(profile.walletKeyDerivationId, profile.profileId);
490
+ const wallet = await this.createWallet(walletKeyDerivationId, seed);
491
+ const context = walletContext(walletKeyDerivationId);
492
+ await requireRegisteredProfileKeys(wallet, context, profile);
493
+ const secureTransportAdapter = {
494
+ pack: (message) => wallet.packForRecipientWithContext(bindOrganizationControllerTransport(message, profile), profile.providerDid, { context }),
495
+ unpack: async (jwe) => (await wallet.unpackWithContext(jwe, { context })).content,
496
+ };
497
+ const operationalClient = new NodeHttpClient({
498
+ baseUrl: this.options.gatewayBaseUrl,
499
+ ctx: profile.routeContext,
500
+ bearerToken: idToken,
501
+ fetchImpl: this.options.fetchImpl,
502
+ appInfo: this.options.appInfo,
503
+ transportProfile: TransportProfiles.DidcommEncryptedForm,
504
+ secureTransportAdapter,
505
+ });
506
+ // SMART/OpenID uses its own JSON/form protocol and must not be wrapped in
507
+ // the encrypted resource transport selected for later business actions.
508
+ const sdk = new ProfessionalSdk(this.createClient(profile.routeContext, idToken));
509
+ const digitalTwin = new DigitalTwinSdk(operationalClient, profile.actorDid);
510
+ let digitalTwinAccessToken;
511
+ const request = async (smart) => {
512
+ if (!smart.scopes?.length)
513
+ throw new Error('Professional SMART request requires scopes.');
514
+ const now = this.now();
515
+ const audience = [
516
+ this.options.gatewayBaseUrl.replace(/\/+$/, ''),
517
+ buildIdentityOpenIdSmartTokenPath(profile.routeContext),
518
+ ].join('');
519
+ const clientAssertion = await buildWalletClientAssertion(wallet, profile, audience, now);
520
+ const vpToken = await buildManagedProfessionalVp(wallet, context, {
521
+ clientId: profile.clientId,
522
+ actorDid: profile.actorDid,
523
+ profileDid: profile.profileDid,
524
+ ...input.professionalProof,
525
+ role,
526
+ });
527
+ return sdk.requestSmartToken({
528
+ ...profile.routeContext,
529
+ actorDid: profile.actorDid,
530
+ subjectDid: String(smart.subjectDid || '').trim() || undefined,
531
+ clientId: profile.clientId,
532
+ issuer: profile.clientId,
533
+ audience,
534
+ idToken,
535
+ vpToken,
536
+ vpTokenFallback: 'omit',
537
+ clientAssertion,
538
+ clientAssertionType: 'private_key_jwt',
539
+ smartTokenKind: 'openid-smart',
540
+ acrValues: smart.acrValues || profileSmartAcrValues(profile.actorKind),
541
+ purpose: smart.purpose,
542
+ scopes: unique(smart.scopes),
543
+ requestBodyClaims: smart.requestBodyClaims,
544
+ tokenCacheKey: smart.tokenCacheKey,
545
+ timeoutSeconds: smart.timeoutSeconds,
546
+ intervalSeconds: smart.intervalSeconds,
547
+ });
548
+ };
549
+ return {
550
+ profile,
551
+ sdk,
552
+ digitalTwin,
553
+ requestSmartToken: request,
554
+ requestDigitalTwinSmartToken: async (smart) => {
555
+ const result = await request(smart);
556
+ if (result.accessToken)
557
+ digitalTwinAccessToken = result.accessToken;
558
+ return result;
559
+ },
560
+ searchDigitalTwins: (search) => {
561
+ if (!digitalTwinAccessToken)
562
+ throw new Error('Digital twin SMART token has not been granted.');
563
+ return digitalTwin.search(profile.routeContext, { ...search, accessToken: digitalTwinAccessToken });
564
+ },
565
+ };
566
+ }
456
567
  async lock(ownerId, sessionId) {
457
568
  const session = await this.options.store.getSession(sessionId);
458
569
  if (session?.ownerId === ownerId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.4.41",
3
+ "version": "2.4.42",
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.",