@reclaimprotocol/client 0.1.27 → 0.1.29
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 +634 -42
- package/lib/api-client.d.ts +31 -0
- package/lib/api-client.js +112 -0
- package/lib/api-consts.d.ts +4 -0
- package/lib/api-consts.js +4 -0
- package/lib/api-errors.d.ts +13 -0
- package/lib/api-errors.js +30 -0
- package/lib/api.d.ts +7 -0
- package/lib/api.js +3 -0
- package/lib/attestor-proof.d.ts +6 -0
- package/lib/attestor-proof.js +45 -18
- package/lib/client.d.ts +1 -6
- package/lib/client.js +12 -33
- package/lib/consts.d.ts +1 -1
- package/lib/consts.js +1 -1
- package/lib/eip191.d.ts +4 -2
- package/lib/eip191.js +17 -1
- package/lib/errors.d.ts +38 -4
- package/lib/errors.js +99 -16
- package/lib/index.d.ts +9 -7
- package/lib/index.js +8 -4
- package/lib/jwks.d.ts +25 -1
- package/lib/jwks.js +69 -4
- package/lib/legacy-proof.d.ts +57 -0
- package/lib/legacy-proof.js +211 -0
- package/lib/proof-hash.d.ts +35 -0
- package/lib/proof-hash.js +216 -0
- package/lib/proof-recipe.d.ts +18 -0
- package/lib/proof-recipe.js +37 -0
- package/lib/proof-trust-constants.d.ts +3 -0
- package/lib/proof-trust-constants.js +43 -0
- package/lib/proof-types.d.ts +62 -0
- package/lib/proof-types.js +1 -0
- package/lib/proof-verification-error.d.ts +13 -0
- package/lib/proof-verification-error.js +11 -0
- package/lib/reclaim.d.ts +27 -5
- package/lib/reclaim.js +140 -14
- package/lib/result.d.ts +58 -6
- package/lib/result.js +252 -61
- package/lib/routes.gen.d.ts +54 -6
- package/lib/routes.gen.js +15 -3
- package/lib/tee-binding.d.ts +82 -0
- package/lib/tee-binding.js +112 -0
- package/lib/types/openapi.gen.d.ts +1343 -206
- package/lib/verification-clients.d.ts +17 -0
- package/lib/verification-clients.js +14 -0
- package/lib/verify-attestor-tee.d.ts +76 -0
- package/lib/verify-attestor-tee.js +243 -0
- package/lib/verify-tee.d.ts +32 -0
- package/lib/verify-tee.js +347 -0
- package/package.json +5 -1
- package/src/api.ts +15 -0
- package/src/attestor-proof.ts +57 -20
- package/src/client.ts +15 -38
- package/src/consts.ts +1 -2
- package/src/eip191.ts +31 -3
- package/src/errors.ts +132 -18
- package/src/index.ts +50 -5
- package/src/jwks.ts +128 -5
- package/src/legacy-proof.ts +367 -0
- package/src/proof-hash.ts +316 -0
- package/src/proof-recipe.ts +75 -0
- package/src/proof-trust-constants.ts +45 -0
- package/src/proof-types.ts +57 -0
- package/src/proof-verification-error.ts +13 -0
- package/src/reclaim.ts +206 -19
- package/src/result.ts +403 -68
- package/src/routes.gen.ts +15 -3
- package/src/tee-binding.ts +192 -0
- package/src/types/openapi.gen.ts +1343 -206
- package/src/verification-clients.ts +22 -0
- package/src/verify-attestor-tee.ts +365 -0
- package/src/verify-tee.ts +507 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
2
|
+
import { canonicalJson as canonical } from "./canonical.js";
|
|
3
|
+
import { bytesToHex } from "./encoding.js";
|
|
4
|
+
/** Exact legacy canonical HTTP-provider hashes, including optional matches. */
|
|
5
|
+
export function hashProofClaimParams(params) {
|
|
6
|
+
return canonicalProofParams(params).map((serialized) => ('0x' + bytesToHex(keccak_256(new TextEncoder().encode(serialized)))));
|
|
7
|
+
}
|
|
8
|
+
export function getHashFromProof(proof, hasNoPii = false) {
|
|
9
|
+
if (hasNoPii) {
|
|
10
|
+
const context = JSON.parse(proof.claimData.context);
|
|
11
|
+
if (typeof context.providerHash !== 'string' || !context.providerHash) {
|
|
12
|
+
throw new Error('Provider hash must be present in proof context');
|
|
13
|
+
}
|
|
14
|
+
return [context.providerHash];
|
|
15
|
+
}
|
|
16
|
+
const parsed = JSON.parse(proof.claimData.parameters);
|
|
17
|
+
if (!isHttpProofParams(parsed)) {
|
|
18
|
+
throw new Error('Proof has no HTTP provider params to hash');
|
|
19
|
+
}
|
|
20
|
+
return hashProofClaimParams(parsed);
|
|
21
|
+
}
|
|
22
|
+
export function hashRequestSpec(request) {
|
|
23
|
+
return {
|
|
24
|
+
value: hashProofClaimParams({
|
|
25
|
+
...request,
|
|
26
|
+
body: request.bodySniff.enabled ? request.bodySniff.template : '',
|
|
27
|
+
}),
|
|
28
|
+
required: request.required,
|
|
29
|
+
multiple: request.multiple,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function hashRequirementsFromSpecs(requestData, allowedInjectedRequestData, proofs) {
|
|
33
|
+
const templates = expandTemplates(allowedInjectedRequestData, templateParametersFromProofs(proofs));
|
|
34
|
+
return [...requestData, ...templates].map(hashRequestSpec);
|
|
35
|
+
}
|
|
36
|
+
export function assertValidProofHashes(proofs, requirements, hasNoPii = false) {
|
|
37
|
+
const unmatched = new Map(proofs.map((proof, index) => ([index, getHashFromProof(proof, hasNoPii)])));
|
|
38
|
+
for (const raw of requirements) {
|
|
39
|
+
const requirement = typeof raw === 'string' ? { value: raw } : raw;
|
|
40
|
+
const expected = (Array.isArray(requirement.value)
|
|
41
|
+
? requirement.value
|
|
42
|
+
: [requirement.value]).map(normalized);
|
|
43
|
+
let found = false;
|
|
44
|
+
for (const [index, hashes] of unmatched) {
|
|
45
|
+
if (!hashes.map(normalized).some((hash) => expected.includes(hash))) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
unmatched.delete(index);
|
|
49
|
+
if (found && requirement.multiple === false) {
|
|
50
|
+
throw new Error(`Proof by hash '${expected.join(',')}' is not allowed `
|
|
51
|
+
+ 'to appear more than once');
|
|
52
|
+
}
|
|
53
|
+
found = true;
|
|
54
|
+
}
|
|
55
|
+
if (!found && requirement.required !== false) {
|
|
56
|
+
throw new Error(`Proof by required hash '${expected.join(',')}' was not found`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (unmatched.size) {
|
|
60
|
+
throw new Error(`${unmatched.size} extra proof(s) could not be validated`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function canonicalProofParams(params) {
|
|
64
|
+
const count = params.responseMatches.length;
|
|
65
|
+
const values = [];
|
|
66
|
+
for (let mask = 0; mask < 2 ** count; mask++) {
|
|
67
|
+
let valid = true;
|
|
68
|
+
const matches = [];
|
|
69
|
+
const redactions = [];
|
|
70
|
+
for (let index = 0; index < count; index++) {
|
|
71
|
+
const match = params.responseMatches[index];
|
|
72
|
+
const redaction = params.responseRedactions[index];
|
|
73
|
+
if ((mask & (1 << index)) === 0) {
|
|
74
|
+
if (match && !match.isOptional) {
|
|
75
|
+
valid = false;
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (match) {
|
|
80
|
+
matches.push({
|
|
81
|
+
value: match.value ?? '',
|
|
82
|
+
type: match.type ?? 'contains',
|
|
83
|
+
invert: match.invert || undefined,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (redaction) {
|
|
87
|
+
redactions.push({
|
|
88
|
+
xPath: redaction.xPath ?? '',
|
|
89
|
+
jsonPath: redaction.jsonPath ?? '',
|
|
90
|
+
regex: redaction.regex ?? '',
|
|
91
|
+
hash: normalizedRedactionHash(redaction.hash),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (valid && matches.length) {
|
|
96
|
+
values.push(canonical({
|
|
97
|
+
url: params.url ?? '',
|
|
98
|
+
method: params.method ?? 'GET',
|
|
99
|
+
body: params.body ?? '',
|
|
100
|
+
responseMatches: matches,
|
|
101
|
+
responseRedactions: redactions,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return values.length ? values : [canonical({
|
|
106
|
+
url: params.url ?? '',
|
|
107
|
+
method: params.method ?? 'GET',
|
|
108
|
+
body: params.body ?? '',
|
|
109
|
+
responseMatches: [],
|
|
110
|
+
responseRedactions: [],
|
|
111
|
+
})];
|
|
112
|
+
}
|
|
113
|
+
function expandTemplates(templates, parameters) {
|
|
114
|
+
const result = [];
|
|
115
|
+
for (const template of templates) {
|
|
116
|
+
const names = template.templateParams ?? [];
|
|
117
|
+
if (!names.length) {
|
|
118
|
+
result.push(template);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const present = names.filter((name) => parameters[name]?.length);
|
|
122
|
+
if (present.length !== names.length) {
|
|
123
|
+
if (template.required === false && !present.length) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
throw new Error('Not all template variables are present');
|
|
127
|
+
}
|
|
128
|
+
const length = parameters[names[0]].length;
|
|
129
|
+
if (!names.every((name) => parameters[name].length === length)) {
|
|
130
|
+
throw new Error('Not all template variables have same length');
|
|
131
|
+
}
|
|
132
|
+
const expanded = Array.from({ length }, (_, index) => substitute(template, Object.fromEntries(names.map((name) => [name, parameters[name][index]]))));
|
|
133
|
+
if (template.templateParamsMode !== 'merge') {
|
|
134
|
+
result.push(...expanded);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
result.push({
|
|
138
|
+
...template,
|
|
139
|
+
responseMatches: expanded.flatMap((item) => item.responseMatches),
|
|
140
|
+
responseRedactions: expanded.flatMap((item) => item.responseRedactions),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
function substitute(template, parameters) {
|
|
146
|
+
const replace = (value) => Object.entries(parameters).reduce((result, [name, replacement]) => (result.split(`\${${name}}`).join(replacement)), value);
|
|
147
|
+
return {
|
|
148
|
+
...template,
|
|
149
|
+
responseMatches: template.responseMatches.map((match) => ({
|
|
150
|
+
...match,
|
|
151
|
+
value: replace(match.value ?? ''),
|
|
152
|
+
})),
|
|
153
|
+
responseRedactions: template.responseRedactions.map((redaction) => ({
|
|
154
|
+
...redaction,
|
|
155
|
+
xPath: replace(redaction.xPath ?? ''),
|
|
156
|
+
jsonPath: replace(redaction.jsonPath ?? ''),
|
|
157
|
+
regex: replace(redaction.regex ?? ''),
|
|
158
|
+
})),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function templateParametersFromProofs(proofs) {
|
|
162
|
+
const combined = {};
|
|
163
|
+
for (const proof of proofs) {
|
|
164
|
+
for (const raw of [proof.claimData.parameters, proof.claimData.context]) {
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(raw);
|
|
167
|
+
Object.assign(combined, parsed.paramValues, parsed.extractedParameters);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// A proof without template parameters is valid.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const result = {};
|
|
175
|
+
for (const [name, raw] of Object.entries(combined)) {
|
|
176
|
+
let value = raw;
|
|
177
|
+
if (typeof value === 'string') {
|
|
178
|
+
try {
|
|
179
|
+
value = JSON.parse(value);
|
|
180
|
+
}
|
|
181
|
+
catch { /* plain string */ }
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(value) && value.length) {
|
|
184
|
+
result[name] = value.map(String);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
function isHttpProofParams(value) {
|
|
190
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
const params = value;
|
|
194
|
+
return typeof params.url === 'string'
|
|
195
|
+
&& typeof params.method === 'string'
|
|
196
|
+
&& ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(params.method)
|
|
197
|
+
&& (params.body === null
|
|
198
|
+
|| params.body === undefined
|
|
199
|
+
|| typeof params.body === 'string')
|
|
200
|
+
&& Array.isArray(params.responseMatches)
|
|
201
|
+
&& params.responseMatches.length > 0
|
|
202
|
+
&& Array.isArray(params.responseRedactions);
|
|
203
|
+
}
|
|
204
|
+
function normalized(value) {
|
|
205
|
+
return value.toLowerCase().trim();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* All OPRF transports prove the same provider-level intent: the selected
|
|
209
|
+
* value is hidden behind an OPRF. Verification Clients may use the transport
|
|
210
|
+
* they implement, so transport spelling must not change the provider hash.
|
|
211
|
+
*/
|
|
212
|
+
function normalizedRedactionHash(value) {
|
|
213
|
+
return value === 'oprf' || value === 'oprf-mpc' || value === 'oprf-raw'
|
|
214
|
+
? '_oprf'
|
|
215
|
+
: value || undefined;
|
|
216
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { components } from './types/openapi.gen.ts';
|
|
2
|
+
import type { LegacyProof } from './proof-types.ts';
|
|
3
|
+
type RequestSelection = components['schemas']['RequestSelection'];
|
|
4
|
+
type RequestSelectionTemplate = components['schemas']['RequestSelectionTemplate'];
|
|
5
|
+
/** Resolved Builder provider recipe returned for a verification session. */
|
|
6
|
+
export interface BuilderProofRecipe {
|
|
7
|
+
providerId: string;
|
|
8
|
+
resolvedVersion: string;
|
|
9
|
+
initialUrl: string;
|
|
10
|
+
requests: RequestSelection[];
|
|
11
|
+
allowedJsRequests?: RequestSelectionTemplate[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Derive the exact hash requirements consumed by legacy `verifyProof` from a
|
|
15
|
+
* resolved Builder recipe. Builder's fixed requests are required once each.
|
|
16
|
+
*/
|
|
17
|
+
export declare function proofHashesFromBuilderRecipe(recipe: BuilderProofRecipe, proofs: LegacyProof[]): import("./proof-types.ts").HashRequirement[];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { hashRequirementsFromSpecs, } from "./proof-hash.js";
|
|
2
|
+
/**
|
|
3
|
+
* Derive the exact hash requirements consumed by legacy `verifyProof` from a
|
|
4
|
+
* resolved Builder recipe. Builder's fixed requests are required once each.
|
|
5
|
+
*/
|
|
6
|
+
export function proofHashesFromBuilderRecipe(recipe, proofs) {
|
|
7
|
+
return hashRequirementsFromSpecs(recipe.requests.map((request) => toRequestSpec(request, { required: true, multiple: false })), (recipe.allowedJsRequests ?? []).map((request) => toRequestSpec(request, {
|
|
8
|
+
required: request.required,
|
|
9
|
+
multiple: request.multiple,
|
|
10
|
+
templateParams: request.templateParams,
|
|
11
|
+
templateParamsMode: request.templateParamsMode,
|
|
12
|
+
})), proofs);
|
|
13
|
+
}
|
|
14
|
+
function toRequestSpec(request, flags) {
|
|
15
|
+
return {
|
|
16
|
+
url: request.url,
|
|
17
|
+
urlType: request.url.includes('{{') ? 'TEMPLATE' : 'CONSTANT',
|
|
18
|
+
method: request.method,
|
|
19
|
+
bodySniff: {
|
|
20
|
+
enabled: request.requestBodyTemplate !== undefined,
|
|
21
|
+
template: request.requestBodyTemplate ?? '',
|
|
22
|
+
},
|
|
23
|
+
responseMatches: (request.responseMatches ?? []).map((match) => ({
|
|
24
|
+
type: match.type,
|
|
25
|
+
value: match.value,
|
|
26
|
+
invert: undefined,
|
|
27
|
+
isOptional: match.isOptional,
|
|
28
|
+
})),
|
|
29
|
+
responseRedactions: (request.responseRedactions ?? []).map((redaction) => ({
|
|
30
|
+
...(redaction.hash ? { hash: redaction.hash } : {}),
|
|
31
|
+
jsonPath: redaction.jsonPath ?? '',
|
|
32
|
+
regex: redaction.regex ?? '',
|
|
33
|
+
xPath: redaction.xPath ?? '',
|
|
34
|
+
})),
|
|
35
|
+
...flags,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const GCP_CONFIDENTIAL_SPACE_ROOT_CA = "-----BEGIN CERTIFICATE-----\n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\nuyT5CnJulxSC5CT1\n-----END CERTIFICATE-----";
|
|
2
|
+
export declare const ATTESTOR_NONCE_PATTERN: RegExp;
|
|
3
|
+
export declare const GCP_CONFIDENTIAL_SPACE_ISSUER = "https://confidentialcomputing.googleapis.com";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* eslint-disable @stylistic/max-len */
|
|
2
|
+
// GCP Confidential Space Root CA — pinned. Rotates infrequently
|
|
3
|
+
// (current cert valid 2024-2034). Used to verify attestor TEE attestation JWTs.
|
|
4
|
+
export const GCP_CONFIDENTIAL_SPACE_ROOT_CA = `-----BEGIN CERTIFICATE-----
|
|
5
|
+
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
|
|
37
|
+
uyT5CnJulxSC5CT1
|
|
38
|
+
-----END CERTIFICATE-----`;
|
|
39
|
+
// Matches an `attestor_public_key:0x<40-hex>` entry in a GCP Confidential
|
|
40
|
+
// Space attestation JWT's `eat_nonce` claim.
|
|
41
|
+
export const ATTESTOR_NONCE_PATTERN = /^attestor_public_key:0x([0-9a-fA-F]{40})$/;
|
|
42
|
+
// Expected `iss` claim on GCP Confidential Space attestation JWTs.
|
|
43
|
+
export const GCP_CONFIDENTIAL_SPACE_ISSUER = 'https://confidentialcomputing.googleapis.com';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Exact legacy proof structures carried inside Builder result envelopes. */
|
|
2
|
+
export interface LegacyProof {
|
|
3
|
+
identifier: string;
|
|
4
|
+
claimData: LegacyClaimData;
|
|
5
|
+
signatures: string[];
|
|
6
|
+
witnesses: LegacyWitness[];
|
|
7
|
+
extractedParameterValues: unknown;
|
|
8
|
+
publicData?: unknown;
|
|
9
|
+
taskId?: number;
|
|
10
|
+
teeAttestation?: TeeAttestation | string;
|
|
11
|
+
}
|
|
12
|
+
export interface LegacyClaimData {
|
|
13
|
+
provider: string;
|
|
14
|
+
parameters: string;
|
|
15
|
+
owner: string;
|
|
16
|
+
timestampS: number;
|
|
17
|
+
context: string;
|
|
18
|
+
identifier: string;
|
|
19
|
+
epoch: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LegacyWitness {
|
|
22
|
+
id: string;
|
|
23
|
+
url: string;
|
|
24
|
+
claimAttestation?: AttestorClaimAttestation;
|
|
25
|
+
}
|
|
26
|
+
export interface AttestorClaimAttestation {
|
|
27
|
+
attestor_address: string;
|
|
28
|
+
claim_signature: string;
|
|
29
|
+
attestation_report: string;
|
|
30
|
+
}
|
|
31
|
+
export interface TeeAttestation {
|
|
32
|
+
proof_version?: 'v2' | 'v3';
|
|
33
|
+
proofVersion?: 'v2' | 'v3';
|
|
34
|
+
tee_provider: string;
|
|
35
|
+
tee_technology: string;
|
|
36
|
+
nonce: string;
|
|
37
|
+
timestamp: string;
|
|
38
|
+
workload: {
|
|
39
|
+
container_name?: string;
|
|
40
|
+
image_digest: string;
|
|
41
|
+
};
|
|
42
|
+
verifier: {
|
|
43
|
+
container_name?: string;
|
|
44
|
+
image_digest: string;
|
|
45
|
+
};
|
|
46
|
+
attestation: {
|
|
47
|
+
token: string;
|
|
48
|
+
};
|
|
49
|
+
error?: {
|
|
50
|
+
code: string;
|
|
51
|
+
message: string;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export interface TrustedData {
|
|
55
|
+
context: Record<string, unknown>;
|
|
56
|
+
extractedParameters: Record<string, string>;
|
|
57
|
+
}
|
|
58
|
+
export interface HashRequirement {
|
|
59
|
+
value: string | string[];
|
|
60
|
+
required?: boolean;
|
|
61
|
+
multiple?: boolean;
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function legacyErrorClass(name: string): {
|
|
2
|
+
new (message?: string, innerError?: unknown): {
|
|
3
|
+
readonly innerError?: unknown;
|
|
4
|
+
name: string;
|
|
5
|
+
message: string;
|
|
6
|
+
stack?: string;
|
|
7
|
+
cause?: unknown;
|
|
8
|
+
};
|
|
9
|
+
isError(error: unknown): error is Error;
|
|
10
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
11
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
12
|
+
stackTraceLimit: number;
|
|
13
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function legacyErrorClass(name) {
|
|
2
|
+
return class extends Error {
|
|
3
|
+
constructor(message, innerError) {
|
|
4
|
+
super(innerError
|
|
5
|
+
? `${message ?? ''} caused by ${String(innerError)}`
|
|
6
|
+
: message);
|
|
7
|
+
this.name = name;
|
|
8
|
+
this.innerError = innerError;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
package/lib/reclaim.d.ts
CHANGED
|
@@ -25,13 +25,14 @@ import type { components } from './types/openapi.gen.ts';
|
|
|
25
25
|
import { ReclaimClient } from './client.ts';
|
|
26
26
|
import { type DecryptionKeyInput } from './enckeys.ts';
|
|
27
27
|
import { type CallbackBody, type Jws, type VerifyResultFullOptions, type VerifyResultFullOutcome } from './result.ts';
|
|
28
|
+
import { type LegacyTeeNonceMode } from './tee-binding.ts';
|
|
28
29
|
type CallbackSubscription = components['schemas']['CallbackSubscription'];
|
|
29
30
|
type VerificationSession = components['schemas']['VerificationSession'];
|
|
30
31
|
type VerificationEvent = components['schemas']['VerificationEvent'];
|
|
31
32
|
type VerificationEventRecord = components['schemas']['VerificationEventRecord'];
|
|
32
33
|
type OrgKeypair = components['schemas']['OrgKeypair'];
|
|
33
|
-
/**
|
|
34
|
-
export declare const TERMINAL_EVENTS: readonly ["verification_success", "verification_rejected", "verification_error"];
|
|
34
|
+
/** Terminal lifecycle events selected by default for callback subscriptions. */
|
|
35
|
+
export declare const TERMINAL_EVENTS: readonly ["verification_success", "verification_rejected", "verification_error", "verification_cancelled", "session_expired"];
|
|
35
36
|
export interface RegisterCallbackInput {
|
|
36
37
|
callbackUrl: string;
|
|
37
38
|
events: VerificationEvent[];
|
|
@@ -44,8 +45,24 @@ export interface CreateSessionInput {
|
|
|
44
45
|
version?: string;
|
|
45
46
|
}[];
|
|
46
47
|
context: Record<string, unknown>;
|
|
47
|
-
/**
|
|
48
|
+
/** Exact registered Verification Client base URL. It may contain a
|
|
49
|
+
* `{sessionId}` placeholder; Builder otherwise adds the session id as a
|
|
50
|
+
* query parameter. Open the returned `verificationUrl`, not this base
|
|
51
|
+
* URL. */
|
|
48
52
|
verificationClientUrl: string;
|
|
53
|
+
/** Optional legacy-compatible TEE session binding. The app secret is the
|
|
54
|
+
* organization's verification private key and never leaves this process.
|
|
55
|
+
* Session creation returns only after Builder stores the signed binding. */
|
|
56
|
+
teeAttestation?: CreateSessionTeeAttestation;
|
|
57
|
+
}
|
|
58
|
+
/** Options intentionally mirror legacy `verifyProof`'s `teeAttestation`
|
|
59
|
+
* vocabulary. `timestamp` is decimal Unix milliseconds; omit it to use now. */
|
|
60
|
+
export interface CreateSessionTeeAttestation {
|
|
61
|
+
appSecret: string;
|
|
62
|
+
timestamp?: string;
|
|
63
|
+
/** `hash` is the default. Use `signature` only for an older Verification
|
|
64
|
+
* Client that expects the 65-byte EIP-191 nonce. */
|
|
65
|
+
nonceMode?: LegacyTeeNonceMode;
|
|
49
66
|
}
|
|
50
67
|
/** Input for `keypair.set`: the org's eth public key as uncompressed hex
|
|
51
68
|
* (`publicKey`, the form wallets/ethers emit) OR as a secp256k1 JWK
|
|
@@ -79,6 +96,11 @@ export interface Reclaim {
|
|
|
79
96
|
/** Create a verification session. `verificationClientUrl` defaults to
|
|
80
97
|
* the Builder's own `/v/{sessionId}` page. */
|
|
81
98
|
create(input: Omit<CreateSessionInput, 'verificationClientUrl'> & Partial<Pick<CreateSessionInput, 'verificationClientUrl'>>): Promise<VerificationSession>;
|
|
99
|
+
/** Add the immutable legacy-compatible TEE context after session
|
|
100
|
+
* creation. Normally use
|
|
101
|
+
* `sessions.create({ teeAttestation: { appSecret } })`; this method
|
|
102
|
+
* supports retrying or explicitly orchestrating the two-step flow. */
|
|
103
|
+
bindTeeNonce(sessionId: string, input: CreateSessionTeeAttestation): Promise<VerificationSession>;
|
|
82
104
|
get(sessionId: string): Promise<VerificationSession>;
|
|
83
105
|
listEvents(sessionId: string): Promise<VerificationEventRecord[]>;
|
|
84
106
|
};
|
|
@@ -86,7 +108,7 @@ export interface Reclaim {
|
|
|
86
108
|
/** Register a callback subscription (one-time setup). Delivery is scoped
|
|
87
109
|
* to the token's org; results are ECIES-encrypted to the org's eth key
|
|
88
110
|
* when `canEncryptResult` is enabled (see `keypair.set`), else plaintext.
|
|
89
|
-
* `events` defaults to all
|
|
111
|
+
* `events` defaults to all terminal lifecycle events. */
|
|
90
112
|
register(input: Partial<RegisterCallbackInput> & Pick<RegisterCallbackInput, 'callbackUrl'>): Promise<CallbackSubscription>;
|
|
91
113
|
list(): Promise<CallbackSubscription[]>;
|
|
92
114
|
remove(subscriptionId: string): Promise<void>;
|
|
@@ -106,7 +128,7 @@ export interface Reclaim {
|
|
|
106
128
|
* the delivery is encrypted. */
|
|
107
129
|
decrypt(body: CallbackBody, credential?: DecryptionKeyInput): Promise<Jws>;
|
|
108
130
|
/** Verify a decrypted JWS: JWKS discovery, ES256K signature,
|
|
109
|
-
* `reclaimSessionId`/`aud
|
|
131
|
+
* `reclaimSessionId`/`aud`, any legacy `exp`, and exact legacy proofs. */
|
|
110
132
|
verify(jws: Jws, opts?: VerifyResultFullOptions): Promise<VerifyResultFullOutcome>;
|
|
111
133
|
/** Handle any callback delivery in one call — the 90% path. Branches on
|
|
112
134
|
* the envelope's `event`: a terminal event is decrypted (with
|