gdc-common-utils-ts 1.4.8 → 1.4.10
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/utils/didcomm-submit-policy.d.ts +10 -0
- package/dist/utils/didcomm-submit-policy.js +15 -0
- package/dist/utils/didcomm-submit.d.ts +34 -0
- package/dist/utils/didcomm-submit.js +75 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/vp-token.d.ts +37 -0
- package/dist/utils/vp-token.js +60 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type CommunicationMode = 'plain' | 'strict' | 'auto-detect';
|
|
2
|
+
export type DidcommSubmissionCapabilities = {
|
|
3
|
+
hasRecipientEncryptionJwk: boolean;
|
|
4
|
+
};
|
|
5
|
+
export type DidcommSubmissionPlan = {
|
|
6
|
+
mode: CommunicationMode;
|
|
7
|
+
submitKind: 'plain' | 'encrypted';
|
|
8
|
+
reason: 'plain-mode' | 'strict-mode' | 'auto-detect-encrypted' | 'auto-detect-plain';
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveDidcommSubmissionPlan(mode: CommunicationMode, capabilities: DidcommSubmissionCapabilities): DidcommSubmissionPlan;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function resolveDidcommSubmissionPlan(mode, capabilities) {
|
|
2
|
+
if (mode === 'plain') {
|
|
3
|
+
return { mode, submitKind: 'plain', reason: 'plain-mode' };
|
|
4
|
+
}
|
|
5
|
+
if (mode === 'strict') {
|
|
6
|
+
if (!capabilities.hasRecipientEncryptionJwk) {
|
|
7
|
+
throw new Error('strict mode requires recipient encryption JWK.');
|
|
8
|
+
}
|
|
9
|
+
return { mode, submitKind: 'encrypted', reason: 'strict-mode' };
|
|
10
|
+
}
|
|
11
|
+
if (capabilities.hasRecipientEncryptionJwk) {
|
|
12
|
+
return { mode, submitKind: 'encrypted', reason: 'auto-detect-encrypted' };
|
|
13
|
+
}
|
|
14
|
+
return { mode, submitKind: 'plain', reason: 'auto-detect-plain' };
|
|
15
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CommunicationMode } from './didcomm-submit-policy';
|
|
2
|
+
export type DidcommFetchInit = {
|
|
3
|
+
method: 'POST';
|
|
4
|
+
headers: Record<string, string>;
|
|
5
|
+
body: string;
|
|
6
|
+
};
|
|
7
|
+
export type DidcommFetchResponse = {
|
|
8
|
+
status: number;
|
|
9
|
+
headers?: {
|
|
10
|
+
get(name: string): string | null;
|
|
11
|
+
};
|
|
12
|
+
json?: () => Promise<unknown>;
|
|
13
|
+
text?: () => Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export type DidcommFetchLike = (url: string, init: DidcommFetchInit) => Promise<DidcommFetchResponse>;
|
|
16
|
+
export type DidcommSubmitInput = {
|
|
17
|
+
mode: CommunicationMode;
|
|
18
|
+
url: string;
|
|
19
|
+
payload: Record<string, unknown>;
|
|
20
|
+
defaultHeaders?: Record<string, string>;
|
|
21
|
+
bearerToken?: string;
|
|
22
|
+
recipientEncryptionJwk?: unknown;
|
|
23
|
+
fetcher: DidcommFetchLike;
|
|
24
|
+
signCompactJws?: (claims: Record<string, unknown>) => Promise<string>;
|
|
25
|
+
encryptCompactJwe?: (compactJws: string, recipientEncryptionJwk: unknown) => Promise<string>;
|
|
26
|
+
};
|
|
27
|
+
export type DidcommSubmitResult = {
|
|
28
|
+
status: number;
|
|
29
|
+
location?: string;
|
|
30
|
+
body: unknown;
|
|
31
|
+
submitKind: 'plain' | 'encrypted';
|
|
32
|
+
contentType: 'application/didcomm-plaintext+json' | 'application/didcomm-encrypted+json';
|
|
33
|
+
};
|
|
34
|
+
export declare function submitDidcomm(input: DidcommSubmitInput): Promise<DidcommSubmitResult>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { resolveDidcommSubmissionPlan } from './didcomm-submit-policy.js';
|
|
2
|
+
function getHeaderValue(headers, name) {
|
|
3
|
+
if (!headers || typeof headers.get !== 'function') {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const value = headers.get(name);
|
|
7
|
+
return value == null ? undefined : value;
|
|
8
|
+
}
|
|
9
|
+
async function parseBody(response) {
|
|
10
|
+
if (typeof response.json === 'function') {
|
|
11
|
+
try {
|
|
12
|
+
return await response.json();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Continue to text fallback.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (typeof response.text === 'function') {
|
|
19
|
+
const raw = await response.text();
|
|
20
|
+
if (!raw)
|
|
21
|
+
return undefined;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return raw;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
export async function submitDidcomm(input) {
|
|
32
|
+
const plan = resolveDidcommSubmissionPlan(input.mode, {
|
|
33
|
+
hasRecipientEncryptionJwk: !!input.recipientEncryptionJwk,
|
|
34
|
+
});
|
|
35
|
+
const headers = {
|
|
36
|
+
...(input.defaultHeaders ?? {}),
|
|
37
|
+
Accept: 'application/json, application/didcomm-plaintext+json, */*',
|
|
38
|
+
};
|
|
39
|
+
let body;
|
|
40
|
+
let contentType;
|
|
41
|
+
if (plan.submitKind === 'plain') {
|
|
42
|
+
contentType = 'application/didcomm-plaintext+json';
|
|
43
|
+
body = JSON.stringify(input.payload);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (!input.signCompactJws) {
|
|
47
|
+
throw new Error('Encrypted DIDComm submission requires signCompactJws callback.');
|
|
48
|
+
}
|
|
49
|
+
if (!input.encryptCompactJwe) {
|
|
50
|
+
throw new Error('Encrypted DIDComm submission requires encryptCompactJwe callback.');
|
|
51
|
+
}
|
|
52
|
+
if (!input.recipientEncryptionJwk) {
|
|
53
|
+
throw new Error('Encrypted DIDComm submission requires recipientEncryptionJwk.');
|
|
54
|
+
}
|
|
55
|
+
const compactJws = await input.signCompactJws(input.payload);
|
|
56
|
+
body = await input.encryptCompactJwe(compactJws, input.recipientEncryptionJwk);
|
|
57
|
+
contentType = 'application/didcomm-encrypted+json';
|
|
58
|
+
}
|
|
59
|
+
headers['Content-Type'] = contentType;
|
|
60
|
+
if (input.bearerToken) {
|
|
61
|
+
headers.Authorization = `Bearer ${input.bearerToken}`;
|
|
62
|
+
}
|
|
63
|
+
const response = await input.fetcher(input.url, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers,
|
|
66
|
+
body,
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
status: response.status,
|
|
70
|
+
location: getHeaderValue(response.headers, 'location'),
|
|
71
|
+
body: await parseBody(response),
|
|
72
|
+
submitKind: plan.submitKind,
|
|
73
|
+
contentType,
|
|
74
|
+
};
|
|
75
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from './content';
|
|
|
6
6
|
export * from './consent';
|
|
7
7
|
export * from './did';
|
|
8
8
|
export * from './didcomm';
|
|
9
|
+
export * from './didcomm-submit';
|
|
10
|
+
export * from './didcomm-submit-policy';
|
|
9
11
|
export * from './format-converter';
|
|
10
12
|
export * from './fhir-cid';
|
|
11
13
|
export * from './jwt';
|
|
@@ -15,3 +17,4 @@ export * from './multibasehash';
|
|
|
15
17
|
export * from './normalize';
|
|
16
18
|
export * from './object-convert';
|
|
17
19
|
export * from './normalize-uuid';
|
|
20
|
+
export * from './vp-token';
|
package/dist/utils/index.js
CHANGED
|
@@ -6,6 +6,8 @@ export * from './content.js';
|
|
|
6
6
|
export * from './consent.js';
|
|
7
7
|
export * from './did.js';
|
|
8
8
|
export * from './didcomm.js';
|
|
9
|
+
export * from './didcomm-submit.js';
|
|
10
|
+
export * from './didcomm-submit-policy.js';
|
|
9
11
|
export * from './format-converter.js';
|
|
10
12
|
export * from './fhir-cid.js';
|
|
11
13
|
export * from './jwt.js';
|
|
@@ -15,3 +17,4 @@ export * from './multibasehash.js';
|
|
|
15
17
|
export * from './normalize.js';
|
|
16
18
|
export * from './object-convert.js';
|
|
17
19
|
export * from './normalize-uuid.js';
|
|
20
|
+
export * from './vp-token.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type VpTokenHeader = {
|
|
2
|
+
alg: string;
|
|
3
|
+
typ?: string;
|
|
4
|
+
kid?: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type VpTokenPayload = {
|
|
8
|
+
iss: string;
|
|
9
|
+
sub?: string;
|
|
10
|
+
aud?: string;
|
|
11
|
+
jti?: string;
|
|
12
|
+
iat?: number;
|
|
13
|
+
exp?: number;
|
|
14
|
+
nonce?: string;
|
|
15
|
+
vp: {
|
|
16
|
+
'@context'?: unknown;
|
|
17
|
+
type?: unknown;
|
|
18
|
+
holder?: string;
|
|
19
|
+
verifiableCredential: string[];
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
};
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
};
|
|
24
|
+
export declare function generateUuidLike(): string;
|
|
25
|
+
export declare function buildEpochWindow(ttlSeconds?: number): {
|
|
26
|
+
iat: number;
|
|
27
|
+
exp: number;
|
|
28
|
+
};
|
|
29
|
+
export declare function createVP(input?: Partial<VpTokenPayload>): VpTokenPayload;
|
|
30
|
+
export declare function addVC(vpPayload: VpTokenPayload, vcJwt: string): VpTokenPayload;
|
|
31
|
+
export declare function prepareForSignature(header: VpTokenHeader, payload: VpTokenPayload): {
|
|
32
|
+
encodedHeader: string;
|
|
33
|
+
encodedPayload: string;
|
|
34
|
+
signingInput: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function prepareBytesForSignature(header: VpTokenHeader, payload: VpTokenPayload): Uint8Array;
|
|
37
|
+
export declare function buildVpTokenCompact(encodedHeader: string, encodedPayload: string, signatureBase64Url: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Content } from './content.js';
|
|
2
|
+
function fallbackId() {
|
|
3
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
4
|
+
return `id-${Date.now()}-${rand}`;
|
|
5
|
+
}
|
|
6
|
+
export function generateUuidLike() {
|
|
7
|
+
const fn = globalThis?.crypto?.randomUUID;
|
|
8
|
+
if (typeof fn === 'function')
|
|
9
|
+
return fn.call(globalThis.crypto);
|
|
10
|
+
return fallbackId();
|
|
11
|
+
}
|
|
12
|
+
export function buildEpochWindow(ttlSeconds = 300) {
|
|
13
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
14
|
+
return { iat, exp: iat + Math.max(1, Math.floor(ttlSeconds)) };
|
|
15
|
+
}
|
|
16
|
+
export function createVP(input) {
|
|
17
|
+
const ttl = input?.exp && input?.iat ? undefined : buildEpochWindow(300);
|
|
18
|
+
const jti = input?.jti || generateUuidLike();
|
|
19
|
+
const nonce = input?.nonce || generateUuidLike();
|
|
20
|
+
const base = {
|
|
21
|
+
iss: String(input?.iss || ''),
|
|
22
|
+
sub: input?.sub,
|
|
23
|
+
aud: input?.aud,
|
|
24
|
+
jti,
|
|
25
|
+
iat: input?.iat ?? ttl?.iat,
|
|
26
|
+
exp: input?.exp ?? ttl?.exp,
|
|
27
|
+
nonce,
|
|
28
|
+
vp: {
|
|
29
|
+
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
30
|
+
type: ['VerifiablePresentation'],
|
|
31
|
+
holder: input?.vp?.holder || input?.iss || '',
|
|
32
|
+
verifiableCredential: [],
|
|
33
|
+
...(input?.vp || {}),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
return base;
|
|
37
|
+
}
|
|
38
|
+
export function addVC(vpPayload, vcJwt) {
|
|
39
|
+
const v = String(vcJwt || '').trim();
|
|
40
|
+
if (!v)
|
|
41
|
+
return vpPayload;
|
|
42
|
+
vpPayload.vp.verifiableCredential.push(v);
|
|
43
|
+
return vpPayload;
|
|
44
|
+
}
|
|
45
|
+
export function prepareForSignature(header, payload) {
|
|
46
|
+
const encodedHeader = Content.objectToRawBase64UrlSafe(header);
|
|
47
|
+
const encodedPayload = Content.objectToRawBase64UrlSafe(payload);
|
|
48
|
+
return {
|
|
49
|
+
encodedHeader,
|
|
50
|
+
encodedPayload,
|
|
51
|
+
signingInput: `${encodedHeader}.${encodedPayload}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function prepareBytesForSignature(header, payload) {
|
|
55
|
+
const { signingInput } = prepareForSignature(header, payload);
|
|
56
|
+
return new TextEncoder().encode(signingInput);
|
|
57
|
+
}
|
|
58
|
+
export function buildVpTokenCompact(encodedHeader, encodedPayload, signatureBase64Url) {
|
|
59
|
+
return `${encodedHeader}.${encodedPayload}.${String(signatureBase64Url || '').trim()}`;
|
|
60
|
+
}
|