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)
|