gdc-sdk-node-ts 2.5.0 → 2.5.1
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/authorized-subject-directory.js +54 -2
- package/dist/node-managed-wallet.d.ts +10 -0
- package/dist/node-managed-wallet.js +40 -0
- package/dist/organization-registration-authorization.d.ts +16 -0
- package/dist/organization-registration-authorization.js +34 -0
- package/dist/server-profile-session.js +12 -1
- package/package.json +1 -1
|
@@ -7,6 +7,9 @@ const CLAIM_ITEM_CONDITION = 'org.schema.IndividualProduct.itemCondition';
|
|
|
7
7
|
const CLAIM_PERSON_EMAIL = 'org.schema.Person.email';
|
|
8
8
|
const CLAIM_PERSON_TELEPHONE = 'org.schema.Person.telephone';
|
|
9
9
|
const CLAIM_ORGANIZATION_SAME_AS = 'org.schema.Organization.sameAs';
|
|
10
|
+
const CLAIM_ORGANIZATION_OWNER_EMAIL = 'org.schema.Organization.owner.email';
|
|
11
|
+
const CLAIM_ORGANIZATION_OWNER_TELEPHONE = 'org.schema.Organization.owner.telephone';
|
|
12
|
+
const CLAIM_ORGANIZATION_MEMBER_ROLE = 'org.schema.Organization.member.role';
|
|
10
13
|
const CLAIM_RELATED_PERSON_ROLE = 'RelatedPerson.role';
|
|
11
14
|
const CLAIM_OCCUPATION_IDENTIFIER = 'org.schema.Person.hasOccupation.identifier.value';
|
|
12
15
|
/**
|
|
@@ -23,6 +26,24 @@ export async function listAuthorizedIndividualSubjectsWithDeps(routeContext, inp
|
|
|
23
26
|
if (!email && !telephone) {
|
|
24
27
|
throw new Error('A verified email or telephone is required to list authorized subjects.');
|
|
25
28
|
}
|
|
29
|
+
const ownerResult = await deps.submitAndPoll(deps.individualOrganizationSearchPath(routeContext), deps.individualOrganizationSearchPollPath(routeContext), {
|
|
30
|
+
thid: input.requestThid || createThreadId('authorized-subject-owner'),
|
|
31
|
+
body: {
|
|
32
|
+
resourceType: 'Bundle',
|
|
33
|
+
type: 'batch',
|
|
34
|
+
data: [{
|
|
35
|
+
type: 'Family-search-v1.0',
|
|
36
|
+
request: { method: 'POST' },
|
|
37
|
+
meta: { claims: {
|
|
38
|
+
'@context': SCHEMA_CONTEXT,
|
|
39
|
+
...(email ? { [CLAIM_ORGANIZATION_OWNER_EMAIL]: email } : {}),
|
|
40
|
+
...(telephone ? { [CLAIM_ORGANIZATION_OWNER_TELEPHONE]: telephone } : {}),
|
|
41
|
+
} },
|
|
42
|
+
}],
|
|
43
|
+
},
|
|
44
|
+
}, input.pollOptions);
|
|
45
|
+
const ownedSubjects = readOwnedSubjects(ownerResult, { email, telephone });
|
|
46
|
+
const subjectsByDid = new Map(ownedSubjects.map((subject) => [subject.subjectDid, subject]));
|
|
26
47
|
const licenseClaims = {
|
|
27
48
|
'@context': SCHEMA_CONTEXT,
|
|
28
49
|
[CLAIM_AUDIENCE_TYPE]: INDIVIDUAL_AUDIENCE_TYPE,
|
|
@@ -42,7 +63,7 @@ export async function listAuthorizedIndividualSubjectsWithDeps(routeContext, inp
|
|
|
42
63
|
},
|
|
43
64
|
}, input.pollOptions);
|
|
44
65
|
const licenses = readAcceptedLicenses(licenseResult);
|
|
45
|
-
const resolved = await Promise.all(licenses.map(async (license) => {
|
|
66
|
+
const resolved = await Promise.all(licenses.filter((license) => !subjectsByDid.has(license.subjectDid)).map(async (license) => {
|
|
46
67
|
const organizationClaims = {
|
|
47
68
|
'@context': SCHEMA_CONTEXT,
|
|
48
69
|
[CLAIM_ORGANIZATION_SAME_AS]: license.subjectDid,
|
|
@@ -74,7 +95,38 @@ export async function listAuthorizedIndividualSubjectsWithDeps(routeContext, inp
|
|
|
74
95
|
subjectClaims,
|
|
75
96
|
};
|
|
76
97
|
}));
|
|
77
|
-
|
|
98
|
+
for (const subject of resolved.filter((value) => Boolean(value))) {
|
|
99
|
+
subjectsByDid.set(subject.subjectDid, subject);
|
|
100
|
+
}
|
|
101
|
+
return [...subjectsByDid.values()];
|
|
102
|
+
}
|
|
103
|
+
function readOwnedSubjects(result, verified) {
|
|
104
|
+
const resources = readData(result).flatMap((entry) => {
|
|
105
|
+
if (entry?.resource?.resourceType === 'Bundle' && Array.isArray(entry.resource.entry)) {
|
|
106
|
+
return entry.resource.entry.map((item) => item?.resource).filter(Boolean);
|
|
107
|
+
}
|
|
108
|
+
return [];
|
|
109
|
+
});
|
|
110
|
+
return resources.flatMap((resource) => {
|
|
111
|
+
const claims = resource?.meta?.claims;
|
|
112
|
+
if (!claims || typeof claims !== 'object')
|
|
113
|
+
return [];
|
|
114
|
+
const ownerEmail = String(claims[CLAIM_ORGANIZATION_OWNER_EMAIL] || '').trim().toLowerCase();
|
|
115
|
+
const ownerTelephone = String(claims[CLAIM_ORGANIZATION_OWNER_TELEPHONE] || '').trim();
|
|
116
|
+
if (!((verified.email && ownerEmail === verified.email) || (verified.telephone && ownerTelephone === verified.telephone))) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
const subjectDid = String(claims[CLAIM_ORGANIZATION_SAME_AS] || '').trim();
|
|
120
|
+
if (!subjectDid)
|
|
121
|
+
return [];
|
|
122
|
+
const role = String(claims[CLAIM_ORGANIZATION_MEMBER_ROLE] || '').trim();
|
|
123
|
+
return [{
|
|
124
|
+
subjectDid,
|
|
125
|
+
...(role ? { role } : {}),
|
|
126
|
+
grantClaims: {},
|
|
127
|
+
subjectClaims: claims,
|
|
128
|
+
}];
|
|
129
|
+
});
|
|
78
130
|
}
|
|
79
131
|
function readAcceptedLicenses(result) {
|
|
80
132
|
const rows = readData(result)[0]?.resource?.data;
|
|
@@ -180,6 +180,16 @@ export declare class NodeManagedWallet implements IWallet {
|
|
|
180
180
|
* Packs one payload into a transport envelope signed and encrypted by the runtime.
|
|
181
181
|
*/
|
|
182
182
|
packForRecipientWithContext(content: any, recipientDidOrJwk: string | JWK, options: WalletPackOptions): Promise<string>;
|
|
183
|
+
/**
|
|
184
|
+
* Packs the first activation/DCR requests before GW can resolve this
|
|
185
|
+
* installation's communication keys from a registered DID document.
|
|
186
|
+
*
|
|
187
|
+
* The embedded public keys are bootstrap response and signature material;
|
|
188
|
+
* callers must still authenticate the HTTP request with the trusted OIDC
|
|
189
|
+
* token and one server-authorized activation code. Post-DCR operations must
|
|
190
|
+
* use `packForRecipientWithContext` so registered key custody is resolved.
|
|
191
|
+
*/
|
|
192
|
+
packProfileBootstrapForRecipientWithContext(content: any, recipientDidOrJwk: string | JWK, options: WalletPackOptions): Promise<string>;
|
|
183
193
|
/**
|
|
184
194
|
* Legacy unpack shape retained for app compatibility.
|
|
185
195
|
*/
|
|
@@ -467,6 +467,46 @@ export class NodeManagedWallet {
|
|
|
467
467
|
key: encryptionKey,
|
|
468
468
|
});
|
|
469
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Packs the first activation/DCR requests before GW can resolve this
|
|
472
|
+
* installation's communication keys from a registered DID document.
|
|
473
|
+
*
|
|
474
|
+
* The embedded public keys are bootstrap response and signature material;
|
|
475
|
+
* callers must still authenticate the HTTP request with the trusted OIDC
|
|
476
|
+
* token and one server-authorized activation code. Post-DCR operations must
|
|
477
|
+
* use `packForRecipientWithContext` so registered key custody is resolved.
|
|
478
|
+
*/
|
|
479
|
+
async packProfileBootstrapForRecipientWithContext(content, recipientDidOrJwk, options) {
|
|
480
|
+
const recipientJwk = typeof recipientDidOrJwk === 'string'
|
|
481
|
+
? await this.resolveRecipientPublicJwk(recipientDidOrJwk)
|
|
482
|
+
: recipientDidOrJwk;
|
|
483
|
+
const signingKey = this.requireManagedKey(options.context, {
|
|
484
|
+
ownerScope: 'runtime',
|
|
485
|
+
purpose: 'comm-signing',
|
|
486
|
+
}, 'sig');
|
|
487
|
+
const encryptionKey = this.requireManagedKey(options.context, {
|
|
488
|
+
ownerScope: 'runtime',
|
|
489
|
+
purpose: 'comm-encryption',
|
|
490
|
+
}, 'enc');
|
|
491
|
+
const compactJws = await this.signCompactJws(options.context, {
|
|
492
|
+
header: {
|
|
493
|
+
typ: 'JWS',
|
|
494
|
+
jwk: signingKey.descriptor.publicJwk,
|
|
495
|
+
},
|
|
496
|
+
claims: content,
|
|
497
|
+
key: { keyId: signingKey.descriptor.kid },
|
|
498
|
+
});
|
|
499
|
+
const senderEncryptionKey = {
|
|
500
|
+
...encryptionKey.descriptor.publicJwk,
|
|
501
|
+
dBytes: encryptionKey.privateMaterial,
|
|
502
|
+
};
|
|
503
|
+
return this.cryptography.encryptJweToCompact(compactJws, {
|
|
504
|
+
enc: 'A256GCM',
|
|
505
|
+
skid: encryptionKey.descriptor.kid,
|
|
506
|
+
jwk: encryptionKey.descriptor.publicJwk,
|
|
507
|
+
cty: 'JWS',
|
|
508
|
+
}, senderEncryptionKey, recipientJwk);
|
|
509
|
+
}
|
|
470
510
|
/**
|
|
471
511
|
* Legacy unpack shape retained for app compatibility.
|
|
472
512
|
*/
|
|
@@ -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
|
+
}
|
|
@@ -44,7 +44,18 @@ export class ServerProfileSessionManager {
|
|
|
44
44
|
const idToken = String(input.idToken || '').trim();
|
|
45
45
|
if (!idToken)
|
|
46
46
|
throw new Error('Profile enrollment requires a signed OIDC idToken for account/email binding.');
|
|
47
|
-
const client =
|
|
47
|
+
const client = new NodeHttpClient({
|
|
48
|
+
baseUrl: this.options.gatewayBaseUrl,
|
|
49
|
+
ctx: input.routeContext,
|
|
50
|
+
bearerToken: idToken,
|
|
51
|
+
appInfo: this.options.appInfo,
|
|
52
|
+
fetchImpl: this.options.fetchImpl,
|
|
53
|
+
transportProfile: TransportProfiles.DidcommEncryptedForm,
|
|
54
|
+
secureTransportAdapter: {
|
|
55
|
+
pack: (message) => wallet.packProfileBootstrapForRecipientWithContext({ ...message, iss: input.actorDid }, input.providerDid, { context }),
|
|
56
|
+
unpack: async (jwe) => (await wallet.unpackWithContext(jwe, { context })).content,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
48
59
|
const redirectUris = input.redirectUris || input.dcrRedirectUris || [];
|
|
49
60
|
const clientName = String(input.clientName || input.dcrClientName || '').trim();
|
|
50
61
|
const activationRequest = createProfileDeviceActivationRequest({
|