@reclaimprotocol/client 0.1.26 → 0.1.28

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.
Files changed (73) hide show
  1. package/README.md +634 -42
  2. package/lib/api-client.d.ts +31 -0
  3. package/lib/api-client.js +112 -0
  4. package/lib/api-consts.d.ts +4 -0
  5. package/lib/api-consts.js +4 -0
  6. package/lib/api-errors.d.ts +13 -0
  7. package/lib/api-errors.js +30 -0
  8. package/lib/api.d.ts +7 -0
  9. package/lib/api.js +3 -0
  10. package/lib/attestor-proof.d.ts +6 -0
  11. package/lib/attestor-proof.js +45 -18
  12. package/lib/client.d.ts +1 -6
  13. package/lib/client.js +12 -33
  14. package/lib/consts.d.ts +1 -1
  15. package/lib/consts.js +1 -1
  16. package/lib/eip191.d.ts +4 -2
  17. package/lib/eip191.js +17 -1
  18. package/lib/errors.d.ts +38 -4
  19. package/lib/errors.js +99 -16
  20. package/lib/index.d.ts +9 -7
  21. package/lib/index.js +8 -4
  22. package/lib/jwks.d.ts +25 -1
  23. package/lib/jwks.js +69 -4
  24. package/lib/legacy-proof.d.ts +57 -0
  25. package/lib/legacy-proof.js +211 -0
  26. package/lib/proof-hash.d.ts +35 -0
  27. package/lib/proof-hash.js +216 -0
  28. package/lib/proof-recipe.d.ts +18 -0
  29. package/lib/proof-recipe.js +37 -0
  30. package/lib/proof-trust-constants.d.ts +3 -0
  31. package/lib/proof-trust-constants.js +43 -0
  32. package/lib/proof-types.d.ts +62 -0
  33. package/lib/proof-types.js +1 -0
  34. package/lib/proof-verification-error.d.ts +13 -0
  35. package/lib/proof-verification-error.js +11 -0
  36. package/lib/reclaim.d.ts +27 -5
  37. package/lib/reclaim.js +140 -14
  38. package/lib/result.d.ts +58 -6
  39. package/lib/result.js +252 -61
  40. package/lib/routes.gen.d.ts +58 -6
  41. package/lib/routes.gen.js +16 -3
  42. package/lib/tee-binding.d.ts +82 -0
  43. package/lib/tee-binding.js +112 -0
  44. package/lib/types/openapi.gen.d.ts +1412 -193
  45. package/lib/verification-clients.d.ts +17 -0
  46. package/lib/verification-clients.js +14 -0
  47. package/lib/verify-attestor-tee.d.ts +76 -0
  48. package/lib/verify-attestor-tee.js +243 -0
  49. package/lib/verify-tee.d.ts +32 -0
  50. package/lib/verify-tee.js +347 -0
  51. package/package.json +5 -1
  52. package/src/api.ts +15 -0
  53. package/src/attestor-proof.ts +57 -20
  54. package/src/client.ts +15 -38
  55. package/src/consts.ts +1 -2
  56. package/src/eip191.ts +31 -3
  57. package/src/errors.ts +132 -18
  58. package/src/index.ts +50 -5
  59. package/src/jwks.ts +128 -5
  60. package/src/legacy-proof.ts +367 -0
  61. package/src/proof-hash.ts +316 -0
  62. package/src/proof-recipe.ts +75 -0
  63. package/src/proof-trust-constants.ts +45 -0
  64. package/src/proof-types.ts +57 -0
  65. package/src/proof-verification-error.ts +13 -0
  66. package/src/reclaim.ts +206 -19
  67. package/src/result.ts +403 -68
  68. package/src/routes.gen.ts +16 -3
  69. package/src/tee-binding.ts +192 -0
  70. package/src/types/openapi.gen.ts +1412 -193
  71. package/src/verification-clients.ts +22 -0
  72. package/src/verify-attestor-tee.ts +365 -0
  73. package/src/verify-tee.ts +507 -0
package/lib/reclaim.js CHANGED
@@ -1,11 +1,21 @@
1
1
  import { ReclaimClient } from "./client.js";
2
2
  import { loadDecryptionKey, } from "./enckeys.js";
3
+ import { isProblemError } from "./errors.js";
4
+ import { proofHashesFromBuilderRecipe } from "./proof-recipe.js";
3
5
  import { decryptCallback, verifyResultFull, } from "./result.js";
4
- /** The three terminal events a subscription can receive. */
6
+ import { createBuilderTeeBinding, } from "./tee-binding.js";
7
+ /** Terminal lifecycle events selected by default for callback subscriptions. */
5
8
  export const TERMINAL_EVENTS = [
6
9
  'verification_success',
7
10
  'verification_rejected',
8
11
  'verification_error',
12
+ 'verification_cancelled',
13
+ 'session_expired',
14
+ ];
15
+ const SIGNED_RESULT_EVENTS = [
16
+ 'verification_success',
17
+ 'verification_rejected',
18
+ 'verification_error',
9
19
  ];
10
20
  export function createReclaim(config) {
11
21
  if (!config?.orgSecret) {
@@ -25,14 +35,23 @@ export function createReclaim(config) {
25
35
  api: client.call.bind(client),
26
36
  sessions: {
27
37
  async create(input) {
38
+ const { teeAttestation, ...request } = input;
28
39
  const body = {
29
- ...input,
30
- verificationClientUrl: input.verificationClientUrl ?? `${client.baseUrl}/v/{sessionId}`,
40
+ ...request,
41
+ verificationClientUrl: request.verificationClientUrl ?? `${client.baseUrl}/v/{sessionId}`,
31
42
  };
32
43
  const { data } = await client.call('CreateVerificationSession', {
33
44
  body,
34
45
  });
35
- return data;
46
+ return teeAttestation
47
+ ? bindTeeNonce(data, teeAttestation)
48
+ : data;
49
+ },
50
+ async bindTeeNonce(sessionId, input) {
51
+ const { data } = await client.call('GetVerificationSession', {
52
+ params: { sessionId },
53
+ });
54
+ return bindTeeNonce(data, input);
36
55
  },
37
56
  async get(sessionId) {
38
57
  const { data } = await client.call('GetVerificationSession', {
@@ -103,7 +122,10 @@ export function createReclaim(config) {
103
122
  return decryptCallback(resolveKey(credential), body);
104
123
  },
105
124
  verify(jws, opts) {
106
- return verifyResultFull(jws, { fetchImpl: config.fetch, ...opts });
125
+ return verifyResultFull(jws, {
126
+ fetchImpl: config.fetch,
127
+ ...withRegisteredIssuer(opts),
128
+ });
107
129
  },
108
130
  async receive(body, opts) {
109
131
  // A delivery envelope tagged with a non-terminal event carries a
@@ -114,7 +136,7 @@ export function createReclaim(config) {
114
136
  && !(body instanceof ArrayBuffer)) {
115
137
  const env = body;
116
138
  if (env.event
117
- && !TERMINAL_EVENTS.includes(env.event)) {
139
+ && !SIGNED_RESULT_EVENTS.includes(env.event)) {
118
140
  const decoded = parseEventData(env.data);
119
141
  return {
120
142
  kind: 'event',
@@ -128,12 +150,121 @@ export function createReclaim(config) {
128
150
  const jws = await decryptCallback(key, body);
129
151
  const result = await verifyResultFull(jws, {
130
152
  fetchImpl: config.fetch,
131
- ...verifyOpts,
153
+ ...withRegisteredIssuer(verifyOpts),
132
154
  });
133
155
  return { kind: 'result', result };
134
156
  },
135
157
  },
136
158
  };
159
+ async function bindTeeNonce(session, input) {
160
+ const binding = createBuilderTeeBinding({
161
+ organizationId: session.orgId,
162
+ organizationVerificationPrivateKey: input.appSecret,
163
+ sessionId: session.id,
164
+ timestamp: input.timestamp ?? Date.now().toString(),
165
+ nonceMode: input.nonceMode,
166
+ });
167
+ const { data } = await client.call('BindVerificationSessionTeeNonce', {
168
+ params: { sessionId: session.id },
169
+ body: {
170
+ attestationNonce: binding.attestationNonce,
171
+ attestationNonceData: binding.attestationNonceData,
172
+ signature: binding.signature,
173
+ },
174
+ });
175
+ return data;
176
+ }
177
+ async function resolvedSession(id) {
178
+ const { data } = await client.call('GetVerificationRecipe', { params: { sessionId: id } });
179
+ return { recipes: data.recipes };
180
+ }
181
+ async function resolvedProvider(providerId, providerVersion, _allowedTags, proofs) {
182
+ let exactVersion = providerVersion;
183
+ if (!exactVersion) {
184
+ const { data: provider } = await client.call('GetProvider', {
185
+ params: { providerId },
186
+ });
187
+ if (!provider.latestVersion) {
188
+ throw new Error(`Provider ${providerId} has no live version`);
189
+ }
190
+ exactVersion = canonicalVersion(provider.latestVersion);
191
+ }
192
+ const { data: version } = await client.call('GetProviderVersion', {
193
+ params: { providerId, version: exactVersion },
194
+ });
195
+ return [{
196
+ hashes: proofHashesFromBuilderRecipe({
197
+ providerId,
198
+ resolvedVersion: exactVersion,
199
+ initialUrl: version.initialUrl,
200
+ requests: version.requests,
201
+ allowedJsRequests: version.allowedJsRequests,
202
+ }, proofs),
203
+ }];
204
+ }
205
+ function withRegisteredIssuer(opts = {}) {
206
+ const sessionId = opts.sessionId ?? opts.expectedReclaimSessionId;
207
+ const requestedProofValidation = opts.proofValidation
208
+ ?? opts.proofVerification;
209
+ const resolvedOpts = {
210
+ ...opts,
211
+ proofValidation: {
212
+ ...requestedProofValidation,
213
+ resolveProvider: requestedProofValidation?.resolveProvider
214
+ ?? resolvedProvider,
215
+ },
216
+ ...(sessionId && !opts.resolveSession && !opts.proofRecipes
217
+ ? { resolveSession: resolvedSession }
218
+ : {}),
219
+ };
220
+ if (opts.signerPublicKey
221
+ || opts.issuer
222
+ || opts.trustedIssuer
223
+ || opts.issuers?.length
224
+ || opts.trustedIssuers?.length
225
+ || opts.resolveIssuers
226
+ || opts.issuerResolver
227
+ || opts.issuersByVerificationClientId
228
+ || opts.jwksUrl
229
+ || opts.jwksUrlsByVerificationClientId
230
+ || opts.resolveJwksUrl) {
231
+ return resolvedOpts;
232
+ }
233
+ return {
234
+ ...resolvedOpts,
235
+ resolveIssuers: async (verificationClientId) => {
236
+ const session = await registeredClient(verificationClientId);
237
+ if (!session) {
238
+ return [];
239
+ }
240
+ return session.verificationClientIssuer
241
+ ? [session.verificationClientIssuer]
242
+ : [];
243
+ },
244
+ resolveJwksUrl: async (verificationClientId) => {
245
+ const session = await registeredClient(verificationClientId);
246
+ return session?.verificationClientJwksUrl;
247
+ },
248
+ };
249
+ async function registeredClient(verificationClientId) {
250
+ if (!sessionId) {
251
+ return undefined;
252
+ }
253
+ const { data: session } = await client.call('GetVerificationSession', { params: { sessionId } });
254
+ if (session.verificationClientId !== verificationClientId) {
255
+ return undefined;
256
+ }
257
+ return session;
258
+ }
259
+ }
260
+ }
261
+ function canonicalVersion(version) {
262
+ const base = `${version.major}.${version.minor}.${version.patch}`;
263
+ return version.branch
264
+ ? `${base}-${version.branch}${version.buildNumber === undefined
265
+ ? ''
266
+ : `.${version.buildNumber}`}`
267
+ : base;
137
268
  }
138
269
  /** Non-terminal `data` is a base64-encoded JSON `{ event, eventData }`. */
139
270
  function parseEventData(data) {
@@ -148,12 +279,7 @@ function parseEventData(data) {
148
279
  return {};
149
280
  }
150
281
  }
151
- /** True for a `404` thrown by {@link ReclaimClient.call} — a Boom error whose
152
- * `output.statusCode` is 404 (see ./errors.ts). */
282
+ /** True for a `404` thrown by {@link ReclaimClient.call}. */
153
283
  function isNotFound(err) {
154
- if (typeof err !== 'object' || err === null) {
155
- return false;
156
- }
157
- const boom = err;
158
- return boom.output?.statusCode === 404 || boom.statusCode === 404;
284
+ return isProblemError(err) && err.output.statusCode === 404;
159
285
  }
package/lib/result.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import type { components } from './types/openapi.gen.ts';
2
2
  import type { DecryptionKey } from './enckeys.ts';
3
3
  import { type Jwk } from './jwks.ts';
4
+ import { type LegacyVerifyProofOptions, type ProofHashRequirement } from './legacy-proof.ts';
5
+ import { type BuilderProofRecipe } from './proof-recipe.ts';
6
+ import type { TrustedData } from './proof-types.ts';
4
7
  export interface Jws {
5
8
  payload: string;
6
9
  signatures: {
@@ -77,18 +80,62 @@ export declare function decodeJwsPayload(jws: Jws): Record<string, unknown>;
77
80
  */
78
81
  export declare function verifyJwsSignature(jws: Jws, signerPublicKey: string): boolean;
79
82
  export interface VerifyResultFullOptions {
83
+ /** Builder session id. Alias for `expectedReclaimSessionId`. */
84
+ sessionId?: string;
80
85
  /** The `reclaimSessionId` the consumer expects (mismatch fails). */
81
86
  expectedReclaimSessionId?: string;
82
87
  /** The consumer's `orgId`; the result's `aud` must match it. */
83
88
  expectedAud?: string;
84
89
  /** Allowlist of trusted attestor ETH addresses (lowercased compared). */
85
90
  attestorAddresses?: string[];
91
+ /** One explicitly trusted Verification Client issuer. */
92
+ issuer?: string;
93
+ /** Alias for `issuer`. */
94
+ trustedIssuer?: string;
95
+ /** Explicitly trusted Verification Client issuers. */
96
+ issuers?: string[];
97
+ /** Alias for `issuers`, retained for security-focused callers. */
98
+ trustedIssuers?: string[];
99
+ /** Resolve registered issuer URLs for the signed session's client id. */
100
+ resolveIssuers?: (verificationClientId: string) => Promise<readonly string[]> | readonly string[];
101
+ /** Alias for `resolveIssuers`. */
102
+ issuerResolver?: (verificationClientId: string) => Promise<readonly string[]> | readonly string[];
103
+ /** Registered issuer URLs keyed by `verificationClientId`. */
104
+ issuersByVerificationClientId?: Record<string, string[]>;
105
+ /** Explicit trusted JWKS URL. This value must come from your registry. */
106
+ jwksUrl?: string;
107
+ /** Trusted JWKS URLs keyed by `verificationClientId`. */
108
+ jwksUrlsByVerificationClientId?: Record<string, string>;
109
+ /** Resolve a registered JWKS URL for the signed session's client id. */
110
+ resolveJwksUrl?: (verificationClientId: string) => Promise<string | undefined> | string | undefined;
111
+ /** Legacy-compatible proof validation options applied to all proofs. */
112
+ proofValidation?: LegacyVerifyProofOptions;
113
+ /** Alias for `proofValidation`. */
114
+ proofVerification?: LegacyVerifyProofOptions;
115
+ /** Per-provider overrides for sessions containing different providers. */
116
+ proofValidationByProviderId?: Record<string, LegacyVerifyProofOptions>;
117
+ /** Legacy-compatible hash requirements, for the common simple case. */
118
+ hashes?: ProofHashRequirement[];
119
+ /** Resolved Builder recipes, used to derive strict proof hashes offline. */
120
+ proofRecipes?: BuilderProofRecipe[];
121
+ /** Resolve a Builder session and its immutable, resolved recipes. */
122
+ resolveSession?: (sessionId: string) => Promise<{
123
+ recipes: BuilderProofRecipe[];
124
+ }> | {
125
+ recipes: BuilderProofRecipe[];
126
+ };
86
127
  /** Override the VC signer key instead of JWKS discovery via header `iss`. */
87
128
  signerPublicKey?: string;
88
129
  /** Injectable fetch (for JWKS); defaults to global `fetch`. */
89
130
  fetchImpl?: typeof fetch;
90
- /** Override "now" (unix seconds) for `exp` checks. */
131
+ /** Override "now" (Unix seconds) when checking a legacy result's `exp`. */
91
132
  now?: number;
133
+ /**
134
+ * Accept a legacy Builder result that contains an expired `exp` claim.
135
+ * New Builder results don't include `exp`; use this only while consuming
136
+ * older stored results whose signatures and proofs must remain verifiable.
137
+ */
138
+ ignoreExpiration?: boolean;
92
139
  }
93
140
  export interface ResultProofOutcome {
94
141
  /** Which provider this proof belongs to (results grouped per provider). */
@@ -97,6 +144,10 @@ export interface ResultProofOutcome {
97
144
  proofValid: boolean;
98
145
  reason?: string;
99
146
  attestorAddress?: string;
147
+ /** Trusted only when `proofValid` is true. */
148
+ data?: TrustedData;
149
+ /** Provider-supplied data. It isn't cryptographically validated. */
150
+ publicData?: unknown;
100
151
  }
101
152
  export interface VerifyResultFullOutcome extends VerifyResultOutcome {
102
153
  proofs?: ResultProofOutcome[];
@@ -126,11 +177,12 @@ export declare class ResultVerificationError extends Error {
126
177
  constructor(outcome: VerifyResultFullOutcome);
127
178
  }
128
179
  /**
129
- * Full consumer verification: discovers the Verification Client's signing key
130
- * via JWKS (header `iss`/`kid`), verifies the top-level JWS, checks
131
- * `reclaimSessionId`/`aud`/`exp`, then verifies each result's nested attestor
132
- * proof and its `content_hash`. Network access (JWKS) makes this async; use
133
- * {@link verifyResult} for the offline primitive.
180
+ * Full consumer verification: resolves a trusted, registered issuer before
181
+ * using JWKS (header `iss`/`kid`), verifies the top-level JWS, checks
182
+ * `reclaimSessionId`/`aud` and any legacy `exp`, then verifies each exact
183
+ * legacy proof.
184
+ * Network access (JWKS and optional provider resolution) makes this async.
185
+ * Use {@link verifyResult} for the offline primitive.
134
186
  */
135
187
  export declare function verifyResultFull(jws: Jws, opts?: VerifyResultFullOptions): Promise<VerifyResultFullOutcome>;
136
188
  /** Decrypt-and-verify in one step. */