gdc-sdk-node-ts 2.3.12 → 2.3.14
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 +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/local-terminology-bff.d.ts +17 -0
- package/dist/local-terminology-bff.js +16 -0
- package/dist/node-managed-wallet.js +3 -1
- package/dist/organization-controller-recovery.d.ts +2 -0
- package/dist/organization-controller-recovery.js +3 -2
- package/dist/server-profile-session.d.ts +26 -0
- package/dist/server-profile-session.js +50 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -572,6 +572,29 @@ Reusable payload examples:
|
|
|
572
572
|
- [gdc-common-utils-ts/src/examples/lifecycle.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/lifecycle.ts)
|
|
573
573
|
- [gdc-common-utils-ts/src/examples/api-flow-examples.ts](https://github.com/Global-DataCare/gdc-common-utils-ts/blob/main/src/examples/api-flow-examples.ts)
|
|
574
574
|
|
|
575
|
+
## Local terminology route for Next.js
|
|
576
|
+
|
|
577
|
+
`LocalTerminologyBffService` keeps complete JSON catalogs in the application
|
|
578
|
+
BFF and returns only matching options:
|
|
579
|
+
|
|
580
|
+
```ts
|
|
581
|
+
const terminology = new LocalTerminologyBffService(catalogs);
|
|
582
|
+
|
|
583
|
+
export async function GET(request: Request) {
|
|
584
|
+
const url = new URL(request.url);
|
|
585
|
+
return Response.json(terminology.search({
|
|
586
|
+
text: url.searchParams.get('text') || '',
|
|
587
|
+
language: url.searchParams.get('language') || 'en',
|
|
588
|
+
jurisdiction: url.searchParams.get('jurisdiction') || undefined,
|
|
589
|
+
systems: url.searchParams.getAll('system'),
|
|
590
|
+
limit: 20,
|
|
591
|
+
}));
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
The application validates which systems are allowed for the specific coded
|
|
596
|
+
form field. This MVP service performs no external terminology request.
|
|
597
|
+
|
|
575
598
|
## API Index
|
|
576
599
|
|
|
577
600
|
## Full Public Surface
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type TerminologyCatalogDocument, type TerminologySearchInput, type TerminologySearchResult } from 'gdc-sdk-core-ts';
|
|
2
|
+
/** Small primary document returned by an application-owned terminology route. */
|
|
3
|
+
export type TerminologySearchPrimaryDocument = Readonly<{
|
|
4
|
+
data: readonly TerminologySearchResult[];
|
|
5
|
+
}>;
|
|
6
|
+
/**
|
|
7
|
+
* Framework-neutral local terminology service for Node/BFF applications.
|
|
8
|
+
*
|
|
9
|
+
* A Next.js route validates its query, calls `search(...)`, and returns the
|
|
10
|
+
* resulting primary document. Complete catalogs remain server-side.
|
|
11
|
+
*/
|
|
12
|
+
export declare class LocalTerminologyBffService {
|
|
13
|
+
private readonly provider;
|
|
14
|
+
constructor(catalogs: readonly TerminologyCatalogDocument[]);
|
|
15
|
+
/** Searches the configured local fallback catalogs. */
|
|
16
|
+
search(input: TerminologySearchInput): TerminologySearchPrimaryDocument;
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LocalTerminologyProvider, } from 'gdc-sdk-core-ts';
|
|
2
|
+
/**
|
|
3
|
+
* Framework-neutral local terminology service for Node/BFF applications.
|
|
4
|
+
*
|
|
5
|
+
* A Next.js route validates its query, calls `search(...)`, and returns the
|
|
6
|
+
* resulting primary document. Complete catalogs remain server-side.
|
|
7
|
+
*/
|
|
8
|
+
export class LocalTerminologyBffService {
|
|
9
|
+
constructor(catalogs) {
|
|
10
|
+
this.provider = new LocalTerminologyProvider(catalogs);
|
|
11
|
+
}
|
|
12
|
+
/** Searches the configured local fallback catalogs. */
|
|
13
|
+
search(input) {
|
|
14
|
+
return { data: this.provider.search(input) };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -486,7 +486,9 @@ export class NodeManagedWallet {
|
|
|
486
486
|
alg: algorithm,
|
|
487
487
|
purpose: `${ownerId}:${purpose}`,
|
|
488
488
|
seed: request.mode === 'deterministic' && request.seedMaterial !== undefined
|
|
489
|
-
?
|
|
489
|
+
? (algorithm.startsWith('ML-DSA')
|
|
490
|
+
? this.deriveSeedBytes(request.seedMaterial, ownerId, purpose, 32)
|
|
491
|
+
: this.deriveSignerSeed(request.seedMaterial, ownerId, purpose))
|
|
490
492
|
: undefined,
|
|
491
493
|
cryptography: this.cryptography,
|
|
492
494
|
});
|
|
@@ -28,4 +28,6 @@ type RecoverOrganizationControllerWithIssueDeps = {
|
|
|
28
28
|
} & Record<string, unknown>, pollOptions?: PollOptions) => Promise<SubmitAndPollResult>;
|
|
29
29
|
};
|
|
30
30
|
export declare function recoverOrganizationControllerWithIssueWithDeps(deps: RecoverOrganizationControllerWithIssueDeps): Promise<OrganizationControllerRecoveryResult>;
|
|
31
|
+
/** Reads the opaque activation code from a successful Organization/_issue poll result. */
|
|
32
|
+
export declare function readOrganizationIssueActivationCode(result: SubmitAndPollResult): string;
|
|
31
33
|
export {};
|
|
@@ -8,7 +8,7 @@ export async function recoverOrganizationControllerWithIssueWithDeps(deps) {
|
|
|
8
8
|
* - this flow must not depend on a new commercial Offer or Order step
|
|
9
9
|
*/
|
|
10
10
|
const issue = await deps.submitLegalOrganizationIssue(deps.hostCtx, deps.input.issueInput, deps.input.issuePollOptions);
|
|
11
|
-
const activationCode =
|
|
11
|
+
const activationCode = readOrganizationIssueActivationCode(issue);
|
|
12
12
|
if (!activationCode) {
|
|
13
13
|
throw new Error('recoverOrganizationControllerWithIssue: missing org.schema.IndividualProduct.serialNumber in Organization/_issue response.');
|
|
14
14
|
}
|
|
@@ -32,7 +32,8 @@ export async function recoverOrganizationControllerWithIssueWithDeps(deps) {
|
|
|
32
32
|
activation,
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
/** Reads the opaque activation code from a successful Organization/_issue poll result. */
|
|
36
|
+
export function readOrganizationIssueActivationCode(result) {
|
|
36
37
|
const pollBody = (result?.poll?.body || {});
|
|
37
38
|
const body = (pollBody.body || pollBody);
|
|
38
39
|
const data = Array.isArray(body.data) ? body.data : Array.isArray(pollBody.data) ? pollBody.data : [];
|
|
@@ -9,6 +9,8 @@ export type ServerActorMode = 'self' | 'controller' | 'member';
|
|
|
9
9
|
/** Durable public metadata plus PIN-and-host protected private material. */
|
|
10
10
|
export type ServerProfileRecord = Readonly<{
|
|
11
11
|
profileId: string;
|
|
12
|
+
/** Stable key-derivation identity. Defaults to profileId for legacy records. */
|
|
13
|
+
walletKeyDerivationId?: string;
|
|
12
14
|
ownerId: string;
|
|
13
15
|
actorKind: ActorKind;
|
|
14
16
|
actorMode: ServerActorMode;
|
|
@@ -64,6 +66,21 @@ export type ServerProfileEnrollmentInput = Readonly<{
|
|
|
64
66
|
idToken: string;
|
|
65
67
|
activationCode: string;
|
|
66
68
|
vpToken: string;
|
|
69
|
+
/**
|
|
70
|
+
* Optional server-only recovery seed. It must be 32 bytes encoded as
|
|
71
|
+
* base64url and must never be accepted from an untrusted browser payload.
|
|
72
|
+
*/
|
|
73
|
+
walletSeed?: string;
|
|
74
|
+
/** Stable identity used to reproduce the same keys independently of profile storage IDs. */
|
|
75
|
+
walletKeyDerivationId?: string;
|
|
76
|
+
}>;
|
|
77
|
+
export type ServerProfileEnrollmentPublicKey = Readonly<{
|
|
78
|
+
ownerScope: string;
|
|
79
|
+
purpose: string;
|
|
80
|
+
use: string;
|
|
81
|
+
alg: string;
|
|
82
|
+
kid: string;
|
|
83
|
+
publicJwk: Record<string, unknown>;
|
|
67
84
|
}>;
|
|
68
85
|
/** One explicit unlock request; scopes and subject remain session-bound. */
|
|
69
86
|
export type ServerProfileUnlockInput = Readonly<{
|
|
@@ -120,6 +137,15 @@ export declare class ServerProfileSessionManager {
|
|
|
120
137
|
private readonly options;
|
|
121
138
|
constructor(options: ServerProfileSessionManagerOptions);
|
|
122
139
|
enroll(input: ServerProfileEnrollmentInput): Promise<ServerProfileRecord>;
|
|
140
|
+
/**
|
|
141
|
+
* Derives only the public enrollment descriptors for a server-governed
|
|
142
|
+
* recovery seed. This is intended for pre-DCR controller binding requests;
|
|
143
|
+
* no private material or seed is returned.
|
|
144
|
+
*/
|
|
145
|
+
prepareEnrollmentPublicKeys(input: Readonly<{
|
|
146
|
+
walletSeed: string;
|
|
147
|
+
walletKeyDerivationId: string;
|
|
148
|
+
}>): Promise<ServerProfileEnrollmentPublicKey[]>;
|
|
123
149
|
listProfiles(ownerId: string): Promise<ServerProfileRecord[]>;
|
|
124
150
|
unlock(input: ServerProfileUnlockInput): Promise<ResolvedServerProfileSession>;
|
|
125
151
|
resolveSession(ownerId: string, sessionId: string): Promise<ResolvedServerProfileSession>;
|
|
@@ -18,9 +18,12 @@ export class ServerProfileSessionManager {
|
|
|
18
18
|
}
|
|
19
19
|
async enroll(input) {
|
|
20
20
|
requireEnrollment(input);
|
|
21
|
-
const seed = randomBytes(32).toString('base64url');
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const seed = input.walletSeed || randomBytes(32).toString('base64url');
|
|
22
|
+
if (input.walletSeed)
|
|
23
|
+
requireBase64UrlSeed32(input.walletSeed);
|
|
24
|
+
const walletKeyDerivationId = normalizedWalletKeyDerivationId(input.walletKeyDerivationId, input.profileId);
|
|
25
|
+
const wallet = await this.createWallet(walletKeyDerivationId, seed);
|
|
26
|
+
const context = walletContext(walletKeyDerivationId);
|
|
24
27
|
const publicKeys = await wallet.getPublicJwks(context, {});
|
|
25
28
|
const storagePublicJwk = publicKeys.find((entry) => entry.purpose === 'document-at-rest')?.publicJwk;
|
|
26
29
|
if (!storagePublicJwk)
|
|
@@ -45,6 +48,7 @@ export class ServerProfileSessionManager {
|
|
|
45
48
|
const now = this.now();
|
|
46
49
|
const record = {
|
|
47
50
|
profileId: input.profileId,
|
|
51
|
+
walletKeyDerivationId,
|
|
48
52
|
ownerId: input.ownerId,
|
|
49
53
|
actorKind: input.actorKind,
|
|
50
54
|
actorMode: input.actorMode,
|
|
@@ -67,6 +71,27 @@ export class ServerProfileSessionManager {
|
|
|
67
71
|
await this.options.store.putProfile(record);
|
|
68
72
|
return record;
|
|
69
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Derives only the public enrollment descriptors for a server-governed
|
|
76
|
+
* recovery seed. This is intended for pre-DCR controller binding requests;
|
|
77
|
+
* no private material or seed is returned.
|
|
78
|
+
*/
|
|
79
|
+
async prepareEnrollmentPublicKeys(input) {
|
|
80
|
+
requireBase64UrlSeed32(input.walletSeed);
|
|
81
|
+
const walletKeyDerivationId = normalizedWalletKeyDerivationId(input.walletKeyDerivationId, '');
|
|
82
|
+
if (!walletKeyDerivationId)
|
|
83
|
+
throw new Error('prepareEnrollmentPublicKeys requires walletKeyDerivationId.');
|
|
84
|
+
const wallet = await this.createWallet(walletKeyDerivationId, input.walletSeed);
|
|
85
|
+
const descriptors = await wallet.getPublicJwks(walletContext(walletKeyDerivationId), {});
|
|
86
|
+
return descriptors.map((entry) => ({
|
|
87
|
+
ownerScope: entry.ownerScope,
|
|
88
|
+
purpose: entry.purpose,
|
|
89
|
+
use: entry.use,
|
|
90
|
+
alg: entry.alg,
|
|
91
|
+
kid: entry.kid,
|
|
92
|
+
publicJwk: entry.publicJwk,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
70
95
|
listProfiles(ownerId) {
|
|
71
96
|
return this.options.store.listProfiles(ownerId);
|
|
72
97
|
}
|
|
@@ -95,7 +120,8 @@ export class ServerProfileSessionManager {
|
|
|
95
120
|
throw new Error('Profile PIN rejected.');
|
|
96
121
|
}
|
|
97
122
|
profile = await this.ensureRequiredStorageProfile(profile, seed);
|
|
98
|
-
const
|
|
123
|
+
const walletKeyDerivationId = profile.walletKeyDerivationId || profile.profileId;
|
|
124
|
+
const wallet = await this.createWallet(walletKeyDerivationId, seed);
|
|
99
125
|
const smartTokenEndpoint = [
|
|
100
126
|
this.options.gatewayBaseUrl.replace(/\/+$/, ''),
|
|
101
127
|
buildIdentityOpenIdSmartTokenPath(profile.routeContext),
|
|
@@ -143,8 +169,9 @@ export class ServerProfileSessionManager {
|
|
|
143
169
|
}
|
|
144
170
|
const profile = await this.requireOwnedProfile(ownerId, session.profileId);
|
|
145
171
|
const seed = await this.options.sealer.unseal(session.sealedUnlockedWalletSeed, `${sessionId}:unlocked-wallet-seed`);
|
|
146
|
-
const
|
|
147
|
-
const
|
|
172
|
+
const walletKeyDerivationId = profile.walletKeyDerivationId || profile.profileId;
|
|
173
|
+
const wallet = await this.createWallet(walletKeyDerivationId, seed);
|
|
174
|
+
const context = walletContext(walletKeyDerivationId);
|
|
148
175
|
return {
|
|
149
176
|
sessionId,
|
|
150
177
|
profile,
|
|
@@ -174,9 +201,9 @@ export class ServerProfileSessionManager {
|
|
|
174
201
|
fetchImpl: this.options.fetchImpl,
|
|
175
202
|
});
|
|
176
203
|
}
|
|
177
|
-
async createWallet(
|
|
204
|
+
async createWallet(walletKeyDerivationId, seed) {
|
|
178
205
|
const wallet = new NodeManagedWallet({ resolveRecipientJwk: this.options.resolveRecipientJwk });
|
|
179
|
-
const context = walletContext(
|
|
206
|
+
const context = walletContext(walletKeyDerivationId);
|
|
180
207
|
await wallet.provisionManagedKeys(context, {
|
|
181
208
|
ownerScope: 'profile',
|
|
182
209
|
purposes: ['actor-signing', 'document-at-rest'],
|
|
@@ -201,8 +228,9 @@ export class ServerProfileSessionManager {
|
|
|
201
228
|
const required = this.options.requiredConfidentialStorageProfile ?? profile.confidentialStorageProfile ?? 'confidential-basic-v1';
|
|
202
229
|
if (required !== 'confidential-pqc-v1')
|
|
203
230
|
return profile;
|
|
204
|
-
const
|
|
205
|
-
const
|
|
231
|
+
const walletKeyDerivationId = profile.walletKeyDerivationId || profile.profileId;
|
|
232
|
+
const wallet = await this.createWallet(walletKeyDerivationId, seed);
|
|
233
|
+
const storageKeys = await wallet.getPublicJwks(walletContext(walletKeyDerivationId), {
|
|
206
234
|
ownerScope: 'profile', purpose: 'document-at-rest', alg: 'ML-KEM-768',
|
|
207
235
|
});
|
|
208
236
|
const storagePublicJwk = storageKeys[0]?.publicJwk;
|
|
@@ -234,7 +262,7 @@ export class ServerProfileSessionManager {
|
|
|
234
262
|
}
|
|
235
263
|
async function buildWalletClientAssertion(wallet, profile, audience, now) {
|
|
236
264
|
const seconds = Math.floor(now.getTime() / 1000);
|
|
237
|
-
return wallet.signCompactJws(walletContext(profile.profileId), {
|
|
265
|
+
return wallet.signCompactJws(walletContext(profile.walletKeyDerivationId || profile.profileId), {
|
|
238
266
|
header: { alg: 'ES384', typ: 'JWT' },
|
|
239
267
|
claims: {
|
|
240
268
|
iss: profile.clientId,
|
|
@@ -247,12 +275,20 @@ async function buildWalletClientAssertion(wallet, profile, audience, now) {
|
|
|
247
275
|
key: { ownerScope: 'runtime', purpose: 'openid-id-token-signing' },
|
|
248
276
|
});
|
|
249
277
|
}
|
|
250
|
-
function walletContext(
|
|
278
|
+
function walletContext(walletKeyDerivationId) {
|
|
251
279
|
return {
|
|
252
|
-
profile: { profileId },
|
|
253
|
-
runtime: { runtimeId: `${
|
|
280
|
+
profile: { profileId: walletKeyDerivationId },
|
|
281
|
+
runtime: { runtimeId: `${walletKeyDerivationId}:server-runtime`, runtimeType: 'backend-service' },
|
|
254
282
|
};
|
|
255
283
|
}
|
|
284
|
+
function normalizedWalletKeyDerivationId(value, profileId) {
|
|
285
|
+
return String(value || profileId).trim();
|
|
286
|
+
}
|
|
287
|
+
function requireBase64UrlSeed32(seed) {
|
|
288
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(seed) || Buffer.from(seed, 'base64url').byteLength !== 32) {
|
|
289
|
+
throw new Error('Profile enrollment walletSeed must be a 32-byte base64url value.');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
256
292
|
function requireEnrollment(input) {
|
|
257
293
|
for (const [name, value] of Object.entries({
|
|
258
294
|
ownerId: input.ownerId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdc-sdk-node-ts",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.14",
|
|
4
4
|
"description": "Next-generation Node runtime package for the GDC SDK family",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Antifraud Services Inc.",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"gdc-common-utils-ts": "^2.3.
|
|
41
|
-
"gdc-sdk-core-ts": "~2.3.
|
|
40
|
+
"gdc-common-utils-ts": "^2.3.18",
|
|
41
|
+
"gdc-sdk-core-ts": "~2.3.13"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^20.14.10",
|