@reclaimprotocol/client 0.1.29 → 0.1.32
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 +255 -234
- package/lib/api.d.ts +2 -0
- package/lib/api.js +2 -0
- package/lib/client.js +2 -0
- package/lib/enckeys.d.ts +1 -1
- package/lib/enckeys.js +2 -2
- package/lib/encoding.d.ts +4 -0
- package/lib/encoding.js +10 -0
- package/lib/feature-flags.d.ts +9 -0
- package/lib/feature-flags.js +19 -0
- package/lib/index.d.ts +4 -4
- package/lib/index.js +4 -4
- package/lib/jwks.d.ts +8 -8
- package/lib/jwks.js +38 -17
- package/lib/launch/destination.d.ts +14 -0
- package/lib/launch/destination.js +89 -0
- package/lib/launch/device.d.ts +30 -0
- package/lib/launch/device.js +168 -0
- package/lib/launch/environment.d.ts +224 -0
- package/lib/launch/environment.js +98 -0
- package/lib/launch/errors.d.ts +20 -0
- package/lib/launch/errors.js +25 -0
- package/lib/launch/extension.d.ts +42 -0
- package/lib/launch/extension.js +72 -0
- package/lib/launch/flow.d.ts +82 -0
- package/lib/launch/flow.js +222 -0
- package/lib/launch/index.d.ts +16 -0
- package/lib/launch/index.js +12 -0
- package/lib/launch/mobile.d.ts +77 -0
- package/lib/launch/mobile.js +124 -0
- package/lib/launch/modal.d.ts +50 -0
- package/lib/launch/modal.js +258 -0
- package/lib/launch/options.d.ts +74 -0
- package/lib/launch/options.js +20 -0
- package/lib/launch/portal.d.ts +28 -0
- package/lib/launch/portal.js +68 -0
- package/lib/launch/qr.d.ts +5 -0
- package/lib/launch/qr.js +28 -0
- package/lib/launch/target.d.ts +46 -0
- package/lib/launch/target.js +128 -0
- package/lib/launch/vendor/qrcodegen.d.ts +134 -0
- package/lib/launch/vendor/qrcodegen.js +822 -0
- package/lib/legacy-proof.d.ts +2 -0
- package/lib/legacy-proof.js +37 -7
- package/lib/proof-hash.d.ts +5 -5
- package/lib/proof-hash.js +1 -1
- package/lib/proof-recipe.d.ts +3 -3
- package/lib/proof-recipe.js +1 -1
- package/lib/proof-types.d.ts +35 -8
- package/lib/reclaim.d.ts +81 -70
- package/lib/reclaim.js +79 -130
- package/lib/request-proof.d.ts +60 -0
- package/lib/request-proof.js +241 -0
- package/lib/result.d.ts +46 -38
- package/lib/result.js +160 -100
- package/lib/routes.gen.d.ts +76 -28
- package/lib/routes.gen.js +24 -11
- package/lib/tee-binding.d.ts +1 -1
- package/lib/types/openapi.d.ts +1 -0
- package/lib/types/openapi.gen.d.ts +1598 -485
- package/lib/types/openapi.gen.js +5 -0
- package/lib/verification-clients.d.ts +28 -14
- package/lib/verification-clients.js +31 -11
- package/lib/verify-attestor-tee.d.ts +19 -2
- package/lib/verify-attestor-tee.js +40 -4
- package/lib/verify-tee.d.ts +10 -1
- package/lib/verify-tee.js +34 -10
- package/package.json +5 -1
- package/src/api.ts +9 -0
- package/src/client.ts +2 -0
- package/src/enckeys.ts +2 -2
- package/src/encoding.ts +12 -0
- package/src/feature-flags.ts +39 -0
- package/src/index.ts +13 -14
- package/src/jwks.ts +52 -30
- package/src/launch/destination.ts +111 -0
- package/src/launch/device.ts +242 -0
- package/src/launch/environment.ts +337 -0
- package/src/launch/errors.ts +49 -0
- package/src/launch/extension.ts +124 -0
- package/src/launch/flow.ts +369 -0
- package/src/launch/index.ts +42 -0
- package/src/launch/mobile.ts +184 -0
- package/src/launch/modal.ts +378 -0
- package/src/launch/options.ts +99 -0
- package/src/launch/portal.ts +104 -0
- package/src/launch/qr.ts +37 -0
- package/src/launch/target.ts +150 -0
- package/src/launch/vendor/qrcodegen.ts +1034 -0
- package/src/proof-hash.ts +6 -6
- package/src/proof-recipe.ts +3 -3
- package/src/proof-types.ts +36 -8
- package/src/reclaim.ts +185 -218
- package/src/{legacy-proof.ts → request-proof.ts} +81 -36
- package/src/result.ts +238 -172
- package/src/routes.gen.ts +24 -11
- package/src/tee-binding.ts +1 -1
- package/src/types/openapi.gen.ts +1603 -485
- package/src/types/openapi.ts +3 -1
- package/src/verification-clients.ts +33 -19
- package/src/verify-attestor-tee.ts +66 -8
- package/src/verify-tee.ts +44 -13
- package/lib/api-client.d.ts +0 -31
- package/lib/api-client.js +0 -112
- package/lib/api-consts.d.ts +0 -4
- package/lib/api-consts.js +0 -4
- package/lib/api-errors.d.ts +0 -13
- package/lib/api-errors.js +0 -30
- package/lib/eip712.d.ts +0 -20
- package/lib/eip712.js +0 -108
package/README.md
CHANGED
|
@@ -6,17 +6,28 @@ order. Integrate it directly into your app.
|
|
|
6
6
|
|
|
7
7
|
One package gives you:
|
|
8
8
|
|
|
9
|
-
- **`
|
|
10
|
-
|
|
9
|
+
- **`ReclaimVerification.create(...)`** — create verification sessions and
|
|
10
|
+
receive and verify results.
|
|
11
11
|
- **`ReclaimClient`** — call any operation in the spec with the low-level typed
|
|
12
12
|
HTTP client (`call('OperationId', args)`).
|
|
13
13
|
- **Crypto helpers** — decrypt ECIES results with the organization's ETH key,
|
|
14
|
-
|
|
15
|
-
proofs.
|
|
14
|
+
verify Builder-signed result JWSs, discover JWKS keys, and verify exact
|
|
15
|
+
proofs. Result signing is Builder-managed in `api=2`.
|
|
16
16
|
|
|
17
17
|
Use the API-only entry point in browsers and edge runtimes. Use the package
|
|
18
18
|
root in trusted runtimes that need proof verification, signing, or encryption.
|
|
19
19
|
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [Choose an entry point](#choose-an-entry-point)
|
|
23
|
+
- [The model in one paragraph](#the-model-in-one-paragraph)
|
|
24
|
+
- [Install](#install)
|
|
25
|
+
- [Quick start](#quick-start-consumer)
|
|
26
|
+
- [API reference](#api-reference)
|
|
27
|
+
- [Crypto and key helpers](#crypto-and-key-helpers)
|
|
28
|
+
- [`ReclaimClient` low-level API](#reclaimclient-low-level)
|
|
29
|
+
- [Security notes](#security-notes)
|
|
30
|
+
|
|
20
31
|
## Choose an entry point
|
|
21
32
|
|
|
22
33
|
Use the narrowest entry point that fits the code:
|
|
@@ -24,7 +35,7 @@ Use the narrowest entry point that fits the code:
|
|
|
24
35
|
| Import | Use it for | Includes |
|
|
25
36
|
| --- | --- | --- |
|
|
26
37
|
| `@reclaimprotocol/client/api` | Browser Verification Clients, frontend dashboards, agents, and backend API calls | Browser-safe `fetch` transport plus OpenAPI-generated operation and schema types |
|
|
27
|
-
| `@reclaimprotocol/client` | Consumer backends and other trusted runtimes | API client, session workflows, result verification, proof verification, signing, and encryption |
|
|
38
|
+
| `@reclaimprotocol/client` | Consumer backends and other trusted runtimes | API client, session workflows, result verification, proof verification, legacy signing, and encryption |
|
|
28
39
|
| `@reclaimprotocol/client/openapi` | Type-only integrations | OpenAPI-generated types only |
|
|
29
40
|
|
|
30
41
|
Both runtime entry points export the exact same `ReclaimClient`,
|
|
@@ -66,8 +77,8 @@ Boom-compatible: `@hapi/boom`'s `isBoom(error)` returns `true`, and
|
|
|
66
77
|
`error.output.statusCode` contains the HTTP status. The API entry point creates
|
|
67
78
|
this shape without importing the Boom runtime into browser bundles.
|
|
68
79
|
|
|
69
|
-
A registered Verification Client uses its session-bound UUID
|
|
70
|
-
|
|
80
|
+
A registered Verification Client uses its session-bound UUID to call the
|
|
81
|
+
bootstrap endpoint:
|
|
71
82
|
|
|
72
83
|
```ts
|
|
73
84
|
import { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
@@ -77,16 +88,43 @@ const builder = new ReclaimClient({
|
|
|
77
88
|
headers: { 'x-reclaim-vc-id': verificationClientId },
|
|
78
89
|
})
|
|
79
90
|
|
|
80
|
-
const { data:
|
|
91
|
+
const { data: bootstrap } = await builder.call('BootstrapVerificationClient', {
|
|
81
92
|
params: { sessionId },
|
|
82
93
|
})
|
|
83
94
|
```
|
|
84
95
|
|
|
96
|
+
Builder also returns an immutable, session-scoped feature configuration on
|
|
97
|
+
`VerificationSession.runtimeConfig`. Read only keys the Verification Client
|
|
98
|
+
implements, and use a safe fallback because older sessions can omit the
|
|
99
|
+
snapshot:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import {
|
|
103
|
+
getBooleanFeatureFlag,
|
|
104
|
+
ReclaimClient,
|
|
105
|
+
} from '@reclaimprotocol/client/api'
|
|
106
|
+
|
|
107
|
+
const { data: bootstrap } = await builder.call('BootstrapVerificationClient', {
|
|
108
|
+
params: { sessionId },
|
|
109
|
+
})
|
|
110
|
+
const useNewPicker = getBooleanFeatureFlag(
|
|
111
|
+
bootstrap.session.runtimeConfig,
|
|
112
|
+
'portal.newRequestPicker',
|
|
113
|
+
false,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The `/api` and package-root entry points export the same browser-safe flag
|
|
118
|
+
readers. Verification Clients never call the admin feature-flag endpoints or
|
|
119
|
+
fetch live values during a session. See
|
|
120
|
+
[Verification Client feature flags](../../docs/verification-feature-flags.md).
|
|
121
|
+
|
|
85
122
|
Treat the Verification Client ID as deployment configuration, not as proof of
|
|
86
|
-
identity. Builder
|
|
87
|
-
|
|
88
|
-
key, or proof-owner private key in browser code.
|
|
89
|
-
|
|
123
|
+
identity. Builder requires it to match the client assigned to the session, but
|
|
124
|
+
the public UUID doesn't authenticate the caller. Never put an organization
|
|
125
|
+
secret, result-signing private key, or proof-owner private key in browser code.
|
|
126
|
+
Builder owns result signing in Builder mode; claimant-facing Verification
|
|
127
|
+
Clients receive no signing key. Call `verifyResultFull` from the consumer
|
|
90
128
|
backend after receiving a result.
|
|
91
129
|
|
|
92
130
|
## The model in one paragraph
|
|
@@ -97,14 +135,13 @@ secrets. An **Ethereum (secp256k1) public key** identifies the organization.
|
|
|
97
135
|
When `canEncryptResult` is enabled, verification **results are encrypted with
|
|
98
136
|
ECIES** (secp256k1 + AES-256-GCM) to the organization's ETH public key. Keep the
|
|
99
137
|
matching **ETH private key** (`0x`-hex, the form any wallet or ethers emits) to
|
|
100
|
-
decrypt.
|
|
101
|
-
against
|
|
138
|
+
decrypt. Builder signs the result JWS with its managed `ES256K` key; verify it
|
|
139
|
+
against the Builder's trusted issuer and well-known JWKS.
|
|
102
140
|
|
|
103
141
|
```
|
|
104
142
|
┌─ one-time setup ─────────────────────────┐
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
canEncryptResult: true }) encrypt to your key
|
|
143
|
+
Configure callback in dashboard or raw API ← where results go
|
|
144
|
+
SetOrgKeypair via dashboard or raw API ← optional encryption
|
|
108
145
|
└──────────────────────────────────────────┘
|
|
109
146
|
┌─ per verification ───────────────────────┐
|
|
110
147
|
reclaim.sessions.create(...) → open session.verificationUrl
|
|
@@ -125,9 +162,9 @@ Configure the client once with your organization secret. In the example,
|
|
|
125
162
|
`orgId` identifies the organization that owns the secret.
|
|
126
163
|
|
|
127
164
|
```ts
|
|
128
|
-
import {
|
|
165
|
+
import { ReclaimVerification } from '@reclaimprotocol/client'
|
|
129
166
|
|
|
130
|
-
const reclaim =
|
|
167
|
+
const reclaim = ReclaimVerification.create({
|
|
131
168
|
orgSecret: process.env.RECLAIM_ORG_SECRET!, // rorg_… (issued by an org owner)
|
|
132
169
|
})
|
|
133
170
|
const orgId = process.env.RECLAIM_ORG_ID! // your org's id
|
|
@@ -139,9 +176,11 @@ Register the endpoint where Builder posts verification results. Complete this
|
|
|
139
176
|
one-time setup with the **dashboard** or the **agent** too; see [Three ways to
|
|
140
177
|
set up](#three-ways-to-set-up).
|
|
141
178
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
179
|
+
The high-level verification client intentionally excludes callback management.
|
|
180
|
+
Use the dashboard, the MCP `create_org_callback_subscription` tool, or call the
|
|
181
|
+
generated `CreateOrgCallbackSubscription` operation with a human-authenticated
|
|
182
|
+
`ReclaimClient` when automating organization setup. The consumer `orgSecret`
|
|
183
|
+
does not authenticate this management operation.
|
|
145
184
|
|
|
146
185
|
### 2. Enable encryption (optional, one-time)
|
|
147
186
|
|
|
@@ -158,9 +197,12 @@ const { privateKeyHex, publicKeyHex } = generateEthKeypair()
|
|
|
158
197
|
|
|
159
198
|
```ts
|
|
160
199
|
// (also doable from the dashboard, which can mint the keypair in-browser)
|
|
161
|
-
await reclaim.
|
|
162
|
-
|
|
163
|
-
|
|
200
|
+
await reclaim.api('SetOrgKeypair', {
|
|
201
|
+
params: { orgId },
|
|
202
|
+
body: {
|
|
203
|
+
publicKey: publicKeyHex,
|
|
204
|
+
canEncryptResult: true,
|
|
205
|
+
},
|
|
164
206
|
})
|
|
165
207
|
```
|
|
166
208
|
|
|
@@ -173,7 +215,7 @@ const session = await reclaim.sessions.create({
|
|
|
173
215
|
// Optional: snapshot this active organization theme onto the session.
|
|
174
216
|
themeId: '550e8400-e29b-41d4-a716-446655440000',
|
|
175
217
|
// Optional: derive and bind the legacy TEE nonce locally after creation.
|
|
176
|
-
|
|
218
|
+
orgEthPrivateKey,
|
|
177
219
|
})
|
|
178
220
|
console.log('open this:', session.verificationUrl) // hand to the end-user
|
|
179
221
|
```
|
|
@@ -184,33 +226,15 @@ console.log('open this:', session.verificationUrl) // hand to the end-user
|
|
|
184
226
|
Verification Client with its registered base URL:
|
|
185
227
|
|
|
186
228
|
```ts
|
|
187
|
-
import {
|
|
188
|
-
KNOWN_VERIFICATION_CLIENTS,
|
|
189
|
-
type KnownVerificationClientUrlMap,
|
|
190
|
-
} from '@reclaimprotocol/client'
|
|
191
|
-
|
|
192
|
-
const verificationClientUrls = {
|
|
193
|
-
[KNOWN_VERIFICATION_CLIENTS.portals]:
|
|
194
|
-
process.env.RECLAIM_PORTAL_URL,
|
|
195
|
-
[KNOWN_VERIFICATION_CLIENTS.verifierApp]:
|
|
196
|
-
process.env.RECLAIM_VERIFIER_APP_URL,
|
|
197
|
-
[KNOWN_VERIFICATION_CLIENTS.inAppSdk]:
|
|
198
|
-
process.env.RECLAIM_INAPP_URL,
|
|
199
|
-
[KNOWN_VERIFICATION_CLIENTS.reclaimBrowserExtension]:
|
|
200
|
-
process.env.RECLAIM_BROWSER_EXTENSION_URL,
|
|
201
|
-
} satisfies KnownVerificationClientUrlMap
|
|
202
|
-
|
|
203
|
-
const clientName = KNOWN_VERIFICATION_CLIENTS.portals
|
|
204
|
-
const verificationClientUrl = verificationClientUrls[clientName]
|
|
205
|
-
if (!verificationClientUrl) throw new Error(`${clientName} URL isn't configured`)
|
|
229
|
+
import { VerificationClient } from '@reclaimprotocol/client'
|
|
206
230
|
|
|
207
231
|
const session = await reclaim.sessions.create({
|
|
208
232
|
providers: [
|
|
209
|
-
// Blank/omitted
|
|
233
|
+
// Blank/omitted → latest; 1.2.2 → exact; ^1.2.2 → >=1.2.2 <2.0.0.
|
|
210
234
|
{ providerId: 'prov_…', version: '^1.2.0' },
|
|
211
235
|
],
|
|
212
236
|
context: { userId: 'abc', orderId: 'order-123' },
|
|
213
|
-
verificationClientUrl,
|
|
237
|
+
verificationClientUrl: VerificationClient.portals,
|
|
214
238
|
})
|
|
215
239
|
|
|
216
240
|
// Builder resolves the URL to a registered client, pins its UUID on the
|
|
@@ -218,6 +242,15 @@ const session = await reclaim.sessions.create({
|
|
|
218
242
|
console.log(session.verificationUrl)
|
|
219
243
|
```
|
|
220
244
|
|
|
245
|
+
Builder resolves each provider independently and returns its exact
|
|
246
|
+
`resolvedVersion`. Verification Clients execute that resolved recipe; they
|
|
247
|
+
don't resolve versions themselves.
|
|
248
|
+
|
|
249
|
+
For imported legacy providers, pass consumer values in `context`. Builder-mode
|
|
250
|
+
clients support `{{context.key}}`, then `{{context_key}}`, then `{{key}}` as
|
|
251
|
+
compatibility aliases. Explicit request or provider-script parameters take
|
|
252
|
+
precedence.
|
|
253
|
+
|
|
221
254
|
Builder snapshots the selected theme at session creation. Omit `themeId` to use
|
|
222
255
|
the organization's most recently updated active theme. If the organization has
|
|
223
256
|
no active theme, the Verification Client uses its default presentation. A
|
|
@@ -238,12 +271,14 @@ enable logs containing personal data, so don't add it to normal production
|
|
|
238
271
|
links. Redirect parameters are also client-owned: append them to the returned
|
|
239
272
|
URL instead of putting them in the session request.
|
|
240
273
|
|
|
241
|
-
Use `{{context.key}}` in new Builder recipes for values supplied in
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
274
|
+
Use `{{context.key}}` in new Builder recipes for values supplied in `context`;
|
|
275
|
+
Builder validates those through the provider's `requiredContext`. Imported
|
|
276
|
+
recipes can fall back to `{{context_key}}` and then `{{key}}`. A lower-priority
|
|
277
|
+
alias is used only when the recipe doesn't use a higher-priority spelling, and
|
|
278
|
+
an explicit parameter always wins. Clients apply this to URLs, headers, request
|
|
279
|
+
bodies, response matches, response redactions, provider scripts, and
|
|
280
|
+
interceptor-created claims. This applies only to `api=2`; it doesn't change
|
|
281
|
+
legacy sessions. Builder-owned
|
|
247
282
|
`reclaimSessionId` and `attestationNonce` remain context-only.
|
|
248
283
|
|
|
249
284
|
The value must exactly equal that deployment's registered
|
|
@@ -253,90 +288,87 @@ yourself. Builder either substitutes it or adds a `sessionId` query parameter,
|
|
|
253
288
|
then adds `api=2`. An unregistered or slightly different URL is rejected with
|
|
254
289
|
HTTP 400.
|
|
255
290
|
|
|
256
|
-
The SDK exposes
|
|
257
|
-
|
|
258
|
-
|
|
291
|
+
The SDK exposes declarative URLs for these intended uses.
|
|
292
|
+
Builder owns terminal-result signing and encryption; claimant-facing clients
|
|
293
|
+
receive no signing key, issuer, JWKS URL, or callback key:
|
|
259
294
|
|
|
260
|
-
| Registry name |
|
|
261
|
-
| --- | --- |
|
|
262
|
-
| `builder` |
|
|
263
|
-
| `portals` |
|
|
264
|
-
| `verifier-app` |
|
|
265
|
-
| `inapp-sdk` |
|
|
266
|
-
| `reclaim-browser-extension` |
|
|
267
|
-
| `zkfetch` |
|
|
295
|
+
| Registry name | Builder v2 use |
|
|
296
|
+
| --- | --- |
|
|
297
|
+
| `builder` | Builder's hosted claimant page at `/v/{sessionId}`. |
|
|
298
|
+
| `portals` | Interactive web portal. The default when you omit the URL. |
|
|
299
|
+
| `verifier-app` | Reclaim Verifier mobile app identifier. |
|
|
300
|
+
| `inapp-sdk` | In-app SDK or add-to-app module identifier. |
|
|
301
|
+
| `reclaim-browser-extension` | Browser extension identifier. |
|
|
302
|
+
| `zkfetch` | Programmatic client in the separate `ZKFETCH` quota pool. |
|
|
268
303
|
|
|
269
|
-
`
|
|
270
|
-
|
|
304
|
+
Use `VerificationClient` to select a built-in client or supply a registered
|
|
305
|
+
custom URL:
|
|
271
306
|
|
|
272
307
|
```ts
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
308
|
+
VerificationClient.builder
|
|
309
|
+
VerificationClient.portals
|
|
310
|
+
VerificationClient.verifierApp
|
|
311
|
+
VerificationClient.inAppSdk
|
|
312
|
+
VerificationClient.reclaimBrowserExtension
|
|
313
|
+
VerificationClient.zkFetch
|
|
314
|
+
VerificationClient.custom('https://verify.example.com')
|
|
281
315
|
```
|
|
282
316
|
|
|
283
|
-
|
|
284
|
-
API and `KnownVerificationClientUrlMap` for deployment configuration. Validate
|
|
285
|
-
untrusted input against the map's values. The names are stable; the URLs are
|
|
286
|
-
not.
|
|
287
|
-
|
|
288
|
-
Client URLs are deployment configuration, not package constants. A seeded
|
|
289
|
-
registry row can be only an identifier until an administrator replaces its URI
|
|
290
|
-
with the real deployed client URL and configures its issuer, JWKS, bridge, and
|
|
291
|
-
signing key. Get the exact value from the Builder administrator or the
|
|
292
|
-
admin-only `/admin/verification-clients` registry; never accept it from a
|
|
293
|
-
claimant or derive it from a signed result. An organization token cannot list
|
|
294
|
-
the admin registry. Administrators can also register additional clients with
|
|
295
|
-
their own URL, issuer, JWKS URL, and signing key.
|
|
317
|
+
Administrators can also register additional clients with their own URL.
|
|
296
318
|
|
|
297
319
|
Examples for the currently supported interactive clients:
|
|
298
320
|
|
|
299
321
|
```ts
|
|
300
|
-
|
|
301
|
-
const registeredClients = {
|
|
302
|
-
portal: 'https://portal.your-deployment.example/',
|
|
303
|
-
verifierApp: 'https://verify.your-deployment.example/app',
|
|
304
|
-
inApp: 'https://app.your-deployment.example/reclaim',
|
|
305
|
-
browserExtension: 'https://extension.your-deployment.example/launch',
|
|
306
|
-
}
|
|
322
|
+
import { VerificationClient } from '@reclaimprotocol/client'
|
|
307
323
|
|
|
308
|
-
// 1.
|
|
309
|
-
const
|
|
324
|
+
// 1. Default (Portal) — omit verificationClientUrl.
|
|
325
|
+
const byDefault = await reclaim.sessions.create({
|
|
310
326
|
providers: [{ providerId }],
|
|
311
327
|
context,
|
|
312
328
|
})
|
|
313
329
|
|
|
314
|
-
// 2. Portal
|
|
330
|
+
// 2. Portal, selected explicitly.
|
|
315
331
|
const portal = await reclaim.sessions.create({
|
|
316
332
|
providers: [{ providerId }],
|
|
317
333
|
context,
|
|
318
|
-
verificationClientUrl:
|
|
334
|
+
verificationClientUrl: VerificationClient.portals,
|
|
319
335
|
})
|
|
320
336
|
|
|
321
|
-
// 3. Reclaim Verifier app
|
|
337
|
+
// 3. Reclaim Verifier app.
|
|
322
338
|
const verifierApp = await reclaim.sessions.create({
|
|
323
339
|
providers: [{ providerId }],
|
|
324
340
|
context,
|
|
325
|
-
verificationClientUrl:
|
|
341
|
+
verificationClientUrl: VerificationClient.verifierApp,
|
|
326
342
|
})
|
|
327
343
|
|
|
328
|
-
// 4. In-app SDK
|
|
344
|
+
// 4. In-app SDK or add-to-app host.
|
|
329
345
|
const inApp = await reclaim.sessions.create({
|
|
330
346
|
providers: [{ providerId }],
|
|
331
347
|
context,
|
|
332
|
-
verificationClientUrl:
|
|
348
|
+
verificationClientUrl: VerificationClient.inAppSdk,
|
|
333
349
|
})
|
|
334
350
|
|
|
335
|
-
// 5. Browser extension
|
|
351
|
+
// 5. Browser extension.
|
|
336
352
|
const browserExtension = await reclaim.sessions.create({
|
|
337
353
|
providers: [{ providerId }],
|
|
338
354
|
context,
|
|
339
|
-
verificationClientUrl:
|
|
355
|
+
verificationClientUrl: VerificationClient.reclaimBrowserExtension,
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
// 6. Builder's hosted claimant page.
|
|
359
|
+
const hosted = await reclaim.sessions.create({
|
|
360
|
+
providers: [{ providerId }],
|
|
361
|
+
context,
|
|
362
|
+
verificationClientUrl: VerificationClient.builder,
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
// 7. Separately registered client or another Builder deployment.
|
|
366
|
+
const custom = await reclaim.sessions.create({
|
|
367
|
+
providers: [{ providerId }],
|
|
368
|
+
context,
|
|
369
|
+
verificationClientUrl: VerificationClient.custom(
|
|
370
|
+
'https://verify.example.com',
|
|
371
|
+
),
|
|
340
372
|
})
|
|
341
373
|
```
|
|
342
374
|
|
|
@@ -385,48 +417,26 @@ sequenceDiagram
|
|
|
385
417
|
Backend-->>Frontend: Return verified status
|
|
386
418
|
```
|
|
387
419
|
|
|
388
|
-
**1. Create the request on your backend.** Keep `RECLAIM_ORG_SECRET` private
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
user or order.
|
|
420
|
+
**1. Create the request on your backend.** Keep `RECLAIM_ORG_SECRET` private
|
|
421
|
+
and save the returned session ID with your user or order. The session opens in
|
|
422
|
+
the Portal unless you pass a `VerificationClient` URL.
|
|
392
423
|
|
|
393
424
|
```ts
|
|
394
425
|
import {
|
|
395
426
|
type CallbackBody,
|
|
396
|
-
|
|
397
|
-
KNOWN_VERIFICATION_CLIENTS,
|
|
398
|
-
type KnownVerificationClientName,
|
|
399
|
-
type KnownVerificationClientUrlMap,
|
|
427
|
+
ReclaimVerification,
|
|
400
428
|
} from '@reclaimprotocol/client'
|
|
401
429
|
|
|
402
|
-
const reclaim =
|
|
430
|
+
const reclaim = ReclaimVerification.create({
|
|
403
431
|
orgSecret: process.env.RECLAIM_ORG_SECRET!,
|
|
404
432
|
})
|
|
405
433
|
|
|
406
|
-
const clientUrls: KnownVerificationClientUrlMap = {
|
|
407
|
-
[KNOWN_VERIFICATION_CLIENTS.portals]: process.env.RECLAIM_PORTAL_URL!,
|
|
408
|
-
[KNOWN_VERIFICATION_CLIENTS.verifierApp]:
|
|
409
|
-
process.env.RECLAIM_VERIFIER_APP_URL!,
|
|
410
|
-
[KNOWN_VERIFICATION_CLIENTS.inAppSdk]: process.env.RECLAIM_INAPP_URL!,
|
|
411
|
-
[KNOWN_VERIFICATION_CLIENTS.reclaimBrowserExtension]:
|
|
412
|
-
process.env.RECLAIM_BROWSER_EXTENSION_URL!,
|
|
413
|
-
}
|
|
414
|
-
|
|
415
434
|
async function createVerificationRequest(
|
|
416
435
|
userId: string,
|
|
417
|
-
clientName: KnownVerificationClientName,
|
|
418
436
|
) {
|
|
419
|
-
const verificationClientUrl = clientName === KNOWN_VERIFICATION_CLIENTS.builder
|
|
420
|
-
? undefined
|
|
421
|
-
: clientUrls[clientName]
|
|
422
|
-
if (clientName !== KNOWN_VERIFICATION_CLIENTS.builder && !verificationClientUrl) {
|
|
423
|
-
throw new Error(`${clientName} URL isn't configured`)
|
|
424
|
-
}
|
|
425
|
-
|
|
426
437
|
const session = await reclaim.sessions.create({
|
|
427
438
|
providers: [{ providerId: 'prov_…', version: '^1.2.0' }],
|
|
428
439
|
context: { userId },
|
|
429
|
-
...(verificationClientUrl ? { verificationClientUrl } : {}),
|
|
430
440
|
})
|
|
431
441
|
await savePendingVerification({
|
|
432
442
|
sessionId: session.id,
|
|
@@ -467,8 +477,21 @@ encoded. Persist the delivery before returning a successful HTTP status so a
|
|
|
467
477
|
process crash can't lose it.
|
|
468
478
|
|
|
469
479
|
```ts
|
|
470
|
-
|
|
471
|
-
|
|
480
|
+
// One-time setup as the signed-in organization member, not with orgSecret.
|
|
481
|
+
import { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
482
|
+
const setupClient = new ReclaimClient({ token: humanAccessToken })
|
|
483
|
+
await setupClient.call('CreateOrgCallbackSubscription', {
|
|
484
|
+
params: { orgId },
|
|
485
|
+
body: {
|
|
486
|
+
callbackUrl: 'https://my.app/reclaim/callback/{{sessionId}}',
|
|
487
|
+
events: [
|
|
488
|
+
'verification_success',
|
|
489
|
+
'verification_rejected',
|
|
490
|
+
'verification_error',
|
|
491
|
+
'verification_cancelled',
|
|
492
|
+
'session_expired',
|
|
493
|
+
],
|
|
494
|
+
},
|
|
472
495
|
})
|
|
473
496
|
|
|
474
497
|
// HTTP callback handler. `sessionId` comes from the route path.
|
|
@@ -482,17 +505,17 @@ async function acceptCallback(sessionId: string, requestBody: CallbackBody) {
|
|
|
482
505
|
Verify the durable delivery in a backend worker. Load the expected session and
|
|
483
506
|
organization from your database; don't take either expected value from the
|
|
484
507
|
callback payload. `results.receive` decrypts when needed, validates the
|
|
485
|
-
|
|
486
|
-
checks the session/audience and any legacy result expiry, and runs the package's
|
|
487
|
-
`
|
|
508
|
+
Builder issuer and JWKS, verifies the outer result JWS,
|
|
509
|
+
checks the session/audience and any legacy result expiry, and runs the package's
|
|
510
|
+
`verifyRequestProof` checks for every proof.
|
|
488
511
|
|
|
489
512
|
```ts
|
|
490
513
|
async function verifyCallbackDelivery(deliveryId: string) {
|
|
491
514
|
const delivery = await loadCallbackDelivery(deliveryId)
|
|
492
515
|
const expected = await loadPendingVerification(delivery.sessionId)
|
|
493
516
|
const outcome = await reclaim.results.receive(delivery.requestBody, {
|
|
494
|
-
|
|
495
|
-
|
|
517
|
+
orgEthPrivateKey: process.env.RECLAIM_ETH_PRIVATE_KEY,
|
|
518
|
+
reclaimSessionId: expected.sessionId,
|
|
496
519
|
expectedAud: expected.orgId,
|
|
497
520
|
})
|
|
498
521
|
|
|
@@ -517,14 +540,15 @@ Removing `api=2` does not turn this request into a valid legacy request. To
|
|
|
517
540
|
create a legacy verification, continue using the legacy JS SDK/backend request
|
|
518
541
|
format. URLs without `api=2` stay on each client's unchanged legacy parser.
|
|
519
542
|
|
|
520
|
-
With `
|
|
543
|
+
With `orgEthPrivateKey`, the SDK creates the session, derives the selected exact
|
|
521
544
|
legacy nonce, signs the Builder binding with
|
|
522
545
|
EIP-191, and registers it at
|
|
523
546
|
`PUT /verifications/sessions/{sessionId}/tee-nonce`. The private key is never
|
|
524
547
|
sent to Builder. The binding is idempotent for identical values and immutable
|
|
525
548
|
afterward; bind before proof generation starts. The default `nonceMode: 'hash'`
|
|
526
549
|
uses `RECLAIM_TEE_NONCE_V1`. Set `nonceMode: 'signature'` for the older
|
|
527
|
-
65-byte EIP-191 nonce that legacy `verifyProof` also accepts.
|
|
550
|
+
65-byte EIP-191 nonce that legacy `verifyProof` also accepts. Use
|
|
551
|
+
`teeNonceMode: 'signature'` on `sessions.create` for this compatibility mode.
|
|
528
552
|
|
|
529
553
|
Because this endpoint is an authenticated POST, you can use `curl` without the
|
|
530
554
|
SDK. The organization secret is the only credential:
|
|
@@ -552,7 +576,7 @@ Never send the private key in either request.
|
|
|
552
576
|
|
|
553
577
|
When the user finishes, Builder sends a POST request to your `callbackUrl`.
|
|
554
578
|
Pass the request body to `results.receive`. It decrypts encrypted results,
|
|
555
|
-
verifies the outer signature, and verifies every exact
|
|
579
|
+
verifies the outer signature, and verifies every exact proof. It
|
|
556
580
|
**throws if the result is invalid**, so reaching the `result` branch means the
|
|
557
581
|
result is verified.
|
|
558
582
|
|
|
@@ -560,12 +584,12 @@ result is verified.
|
|
|
560
584
|
// Load these expected values from your database, not from the callback body.
|
|
561
585
|
const expected = await loadPendingVerification(callbackRouteSessionId)
|
|
562
586
|
const outcome = await reclaim.results.receive(requestBody, {
|
|
563
|
-
|
|
564
|
-
|
|
587
|
+
orgEthPrivateKey: process.env.RECLAIM_ETH_PRIVATE_KEY,
|
|
588
|
+
reclaimSessionId: expected.sessionId,
|
|
565
589
|
expectedAud: expected.orgId,
|
|
566
590
|
})
|
|
567
591
|
if (outcome.kind === 'result') {
|
|
568
|
-
// These values came from
|
|
592
|
+
// These values came from verifyRequestProof. Don't trust diagnostic copies from
|
|
569
593
|
// the outer payload for application decisions.
|
|
570
594
|
const trustedClaims = outcome.result.proofs?.flatMap((proof) => (
|
|
571
595
|
proof.data ? [proof.data] : []
|
|
@@ -580,7 +604,9 @@ if (outcome.kind === 'result') {
|
|
|
580
604
|
|
|
581
605
|
Choose any of these one-time setup methods; each uses the same API:
|
|
582
606
|
|
|
583
|
-
- **
|
|
607
|
+
- **Raw OpenAPI client** — use a human-authenticated `ReclaimClient` for
|
|
608
|
+
`CreateOrgCallbackSubscription`. Use `SetOrgKeypair` through `reclaim.api`
|
|
609
|
+
when configuring result encryption, which is useful for scripting or CI.
|
|
584
610
|
- **The dashboard** — the organization's web console has a callback-subscription form and a "Set encryption keypair" dialog that can **generate the ETH keypair in your browser** and download the private key for you.
|
|
585
611
|
- **The agent** (`@reclaimprotocol/agent` — the `reclaim` CLI / MCP server) — handy while building and testing a provider locally.
|
|
586
612
|
|
|
@@ -592,10 +618,10 @@ authentication and the ETH private key for decryption. The
|
|
|
592
618
|
|
|
593
619
|
## API reference
|
|
594
620
|
|
|
595
|
-
### `
|
|
621
|
+
### `ReclaimVerification.create(config)`
|
|
596
622
|
|
|
597
|
-
Call `
|
|
598
|
-
|
|
623
|
+
Call `ReclaimVerification.create(config)` to return a client with `sessions`
|
|
624
|
+
and `results` namespaces, plus an `api` escape hatch.
|
|
599
625
|
|
|
600
626
|
| `ReclaimConfig` field | type | required | notes |
|
|
601
627
|
|---|---|---|---|
|
|
@@ -604,47 +630,51 @@ Call `createReclaim(config)` to return a `Reclaim` with four namespaces —
|
|
|
604
630
|
| `fetch` | `typeof fetch` | no | Custom fetch (tests, edge runtimes). |
|
|
605
631
|
| `headers` | `Record<string,string>` | no | Extra headers merged into every request. |
|
|
606
632
|
|
|
607
|
-
`orgSecret` is the only required configuration value. Pass
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
and the **ETH private key** to `results.receive`/`decrypt`.
|
|
633
|
+
`orgSecret` is the only required configuration value. Pass the optional ETH
|
|
634
|
+
private key directly to `sessions.create`, `results.receive`, or
|
|
635
|
+
`results.decrypt`. The SDK doesn't cache or serialize that key.
|
|
611
636
|
|
|
612
|
-
|
|
637
|
+
Use `reclaim.export()` to serialize non-secret configuration. Restore it with
|
|
638
|
+
runtime credentials:
|
|
613
639
|
|
|
614
640
|
```ts
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
641
|
+
const saved = reclaim.export()
|
|
642
|
+
const restored = ReclaimVerification.import(saved, { orgSecret })
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
The JSON contains only a format version and Builder base URL. It never contains
|
|
646
|
+
the organization secret, ETH private key, headers, or custom `fetch` function.
|
|
647
|
+
|
|
648
|
+
### Verification clients
|
|
649
|
+
|
|
650
|
+
```ts
|
|
651
|
+
import { VerificationClient } from '@reclaimprotocol/client'
|
|
620
652
|
```
|
|
621
653
|
|
|
622
|
-
- `
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
- `KnownVerificationClientUrlMap` is a partial map from those names to the
|
|
627
|
-
exact URLs registered in one Builder deployment.
|
|
654
|
+
- `VerificationClient.builder`, `.portals`, `.verifierApp`, `.inAppSdk`,
|
|
655
|
+
`.reclaimBrowserExtension`, and `.zkFetch` contain production URLs.
|
|
656
|
+
- `VerificationClient.custom(uri)` returns a separately registered client URL
|
|
657
|
+
unchanged.
|
|
628
658
|
|
|
629
|
-
The
|
|
630
|
-
|
|
659
|
+
The helper doesn't make a URL trusted or register it in Builder. Your backend
|
|
660
|
+
chooses an already registered URL, and
|
|
631
661
|
`sessions.create({ verificationClientUrl })` asks Builder to resolve that URL
|
|
632
|
-
to its registered client UUID. Omit the URL to select `
|
|
662
|
+
to its registered client UUID. Omit the URL to select `portals`.
|
|
633
663
|
|
|
634
664
|
### `reclaim.sessions`
|
|
635
665
|
|
|
636
666
|
```ts
|
|
637
|
-
reclaim.sessions.create({ providers, context, verificationClientUrl?,
|
|
667
|
+
reclaim.sessions.create({ providers, context, verificationClientUrl?, orgEthPrivateKey? })
|
|
638
668
|
// providers: { providerId, version? }[] — proved in order, one combined result; all required.
|
|
639
669
|
// → VerificationSession (has `id`, `verificationUrl`, `status`, `mode`, …)
|
|
640
|
-
// verificationClientUrl selects a registered Verification Client by URL
|
|
641
|
-
//
|
|
642
|
-
//
|
|
670
|
+
// verificationClientUrl selects a registered Verification Client by URL;
|
|
671
|
+
// omitted, it defaults to the Portal.
|
|
672
|
+
// orgEthPrivateKey optionally creates the post-create TEE binding locally.
|
|
643
673
|
// The returned verificationUrl contains sessionId and api=2.
|
|
644
|
-
reclaim.sessions.bindTeeNonce(
|
|
674
|
+
reclaim.sessions.bindTeeNonce(reclaimSessionId, { orgEthPrivateKey, timestamp? })
|
|
645
675
|
// Explicit two-step/retry form. Reads the session, derives locally, and binds.
|
|
646
|
-
reclaim.sessions.get(
|
|
647
|
-
reclaim.sessions.listEvents(
|
|
676
|
+
reclaim.sessions.get(reclaimSessionId) // → VerificationSession
|
|
677
|
+
reclaim.sessions.listEvents(reclaimSessionId) // → VerificationEventRecord[]
|
|
648
678
|
```
|
|
649
679
|
|
|
650
680
|
The Builder resolves `verificationClientUrl` to the session's registered
|
|
@@ -654,45 +684,21 @@ the selected Verification Client understands; the Builder does not interpret
|
|
|
654
684
|
or execute them. A URL without `api=2` is handled by the selected client's
|
|
655
685
|
legacy path.
|
|
656
686
|
|
|
657
|
-
### `reclaim.callbacks`
|
|
658
|
-
|
|
659
|
-
```ts
|
|
660
|
-
reclaim.callbacks.register({ callbackUrl, events? }) // the dashboard preselects 5 terminal events
|
|
661
|
-
// → CallbackSubscription. Org-scoped; no address/publicKey/secret.
|
|
662
|
-
reclaim.callbacks.list() // → CallbackSubscription[]
|
|
663
|
-
reclaim.callbacks.remove(subscriptionId) // → void
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
`events` is any subset of `VerificationEvent`. Builder terminal events are
|
|
667
|
-
`verification_success`, `verification_rejected`, `verification_error`,
|
|
668
|
-
`verification_cancelled`, and `session_expired`. The dashboard preselects all
|
|
669
|
-
five. Terminal result deliveries contain the signed result when one is
|
|
670
|
-
available. Treat cancellation and expiry as terminal outcomes.
|
|
671
|
-
Non-terminal events deliver plaintext progress notifications.
|
|
672
|
-
|
|
673
|
-
### `reclaim.keypair` (the organization's single ETH encryption key)
|
|
674
|
-
|
|
675
|
-
```ts
|
|
676
|
-
await reclaim.keypair.set(orgId, { publicKey, canEncryptResult?: true, label?: 'prod' }) // → OrgKeypair
|
|
677
|
-
await reclaim.keypair.get(orgId) // → OrgKeypair | undefined (undefined ⇒ keyless / plaintext delivery)
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
`publicKey` is the organization's ETH **uncompressed public key** (`0x04` +
|
|
681
|
-
128 hex — the form `generateEthKeypair()` and ethers produce). Alternatively,
|
|
682
|
-
pass `publicKeyJwk` (a secp256k1 JWK). `set` stores the JWK and derived ETH
|
|
683
|
-
address. There is exactly **one** keypair per organization; `set` replaces it
|
|
684
|
-
(rotation). `canEncryptResult` toggles whether results are encrypted to it
|
|
685
|
-
(default `false` ⇒ plaintext). Keep the matching **ETH private key** (`0x`-hex)
|
|
686
|
-
and pass it as the decryption key to `results.receive`/`decrypt`.
|
|
687
|
-
|
|
688
687
|
### `reclaim.results`
|
|
689
688
|
|
|
690
689
|
```ts
|
|
691
690
|
reclaim.results.receive(body, opts?) // → ReceivedDelivery ← the 90% path
|
|
692
|
-
reclaim.results.decrypt(body,
|
|
691
|
+
reclaim.results.decrypt(body, orgEthPrivateKey?) // → Jws
|
|
693
692
|
reclaim.results.verify(jws, opts?) // → VerifyResultFullOutcome
|
|
694
693
|
```
|
|
695
694
|
|
|
695
|
+
Each verified `result.proofs` entry includes the exact `proof` received
|
|
696
|
+
from the Verification Client, its provider and request identity, all attestor
|
|
697
|
+
addresses, derived proof hashes, trusted `data`, unverified `publicData`, and
|
|
698
|
+
the claimant/session and attestor TEE verification outcomes. This preserves the
|
|
699
|
+
complete proof for storage or on-chain use while keeping trusted
|
|
700
|
+
extracted parameters separate from unverified convenience data.
|
|
701
|
+
|
|
696
702
|
`receive` branches on the delivery:
|
|
697
703
|
|
|
698
704
|
```ts
|
|
@@ -701,23 +707,21 @@ type ReceivedDelivery =
|
|
|
701
707
|
| { kind: 'event'; event: VerificationEvent; eventData: Record<string, unknown> } // plaintext lifecycle event
|
|
702
708
|
```
|
|
703
709
|
|
|
704
|
-
`
|
|
705
|
-
|
|
706
|
-
`
|
|
707
|
-
|
|
708
|
-
variable name). All three methods are async; pass `credential` only when the
|
|
709
|
-
delivery is encrypted.
|
|
710
|
+
`ReceiveOptions` includes the `reclaimSessionId` check, `expectedAud`, trusted
|
|
711
|
+
issuer configuration, attestor allowlists, and an optional
|
|
712
|
+
`orgEthPrivateKey`. The private key decrypts encrypted results and verifies TEE
|
|
713
|
+
proof binding. All three methods are async.
|
|
710
714
|
|
|
711
|
-
`verifyResultFull` is the recommended server-side API. Configure
|
|
712
|
-
|
|
713
|
-
|
|
715
|
+
`verifyResultFull` is the recommended server-side API. Configure the canonical
|
|
716
|
+
Builder issuer and well-known JWKS URL (or keep those values in your own
|
|
717
|
+
server-side trust configuration):
|
|
714
718
|
|
|
715
719
|
```ts
|
|
716
720
|
await reclaim.results.verify(jws, {
|
|
717
|
-
|
|
721
|
+
reclaimSessionId: session.id,
|
|
718
722
|
expectedAud: orgId,
|
|
719
|
-
|
|
720
|
-
|
|
723
|
+
trustedIssuers: ['https://build.reclaimprotocol.org'],
|
|
724
|
+
jwksUrl: 'https://build.reclaimprotocol.org/.well-known/jwks.json',
|
|
721
725
|
})
|
|
722
726
|
```
|
|
723
727
|
|
|
@@ -726,21 +730,36 @@ fetches JWKS. It never uses an untrusted `iss` as a network destination. Keep
|
|
|
726
730
|
issuer resolution server-side and treat a missing or changed issuer as a
|
|
727
731
|
verification failure.
|
|
728
732
|
|
|
729
|
-
Issuer trust and key discovery are separate inputs
|
|
730
|
-
|
|
731
|
-
pass `trustedIssuers` and `jwksUrl`). Do not construct the JWKS URL from the
|
|
732
|
-
JWS payload.
|
|
733
|
+
Issuer trust and key discovery are separate Builder deployment inputs. Do not
|
|
734
|
+
construct the JWKS URL from the JWS payload.
|
|
733
735
|
|
|
734
|
-
`verifyResultFull` calls this package's
|
|
736
|
+
`verifyResultFull` calls this package's `verifyRequestProof`
|
|
735
737
|
implementation for every exact `Proof` object in the signed result. It does
|
|
736
738
|
not base64-decode, wrap, reconstruct, or otherwise transform those proofs.
|
|
739
|
+
When you provide `reclaimSessionId`, every proof must
|
|
740
|
+
also contain that session ID. This prevents a valid proof from an older session
|
|
741
|
+
from being placed in a newly signed result envelope. For an older deployment
|
|
742
|
+
that produced unbound proofs, set `proofValidation.requireSessionBinding` to
|
|
743
|
+
`false` explicitly during its migration. A different embedded session ID is
|
|
744
|
+
always rejected.
|
|
737
745
|
By default, it uses each result's
|
|
738
|
-
`provider_id` and exact `resolved_version` to resolve the
|
|
746
|
+
`provider_id` and exact `resolved_version` to resolve the provider hash
|
|
739
747
|
requirements. You can instead pass `hashes`, `proofValidation`, or
|
|
740
|
-
`proofValidationByProviderId`. These options mirror
|
|
748
|
+
`proofValidationByProviderId`. These options mirror `verifyRequestProof`,
|
|
741
749
|
including `hasNoPii`, verifier TEE (`teeAttestation.appSecret`), and
|
|
742
|
-
attestor-TEE checks. The
|
|
743
|
-
purpose-bound result keys
|
|
750
|
+
attestor-TEE checks. The configured Builder JWKS URL supplies the
|
|
751
|
+
purpose-bound result keys, its `x-reclaim-attestors` signer list, and its
|
|
752
|
+
`x-reclaim-attestor-image-digests` Confidential Space image allowlist. When
|
|
753
|
+
the JWKS publishes image digests, `verifyResultFull` automatically verifies
|
|
754
|
+
every witness's attestor TEE `claimAttestation` — its attestor address and
|
|
755
|
+
claim signature always, and its `attestation_report` when the witness carries
|
|
756
|
+
one. The report is optional: production attestors sign claims without
|
|
757
|
+
returning one, so a witness with only an address and a signature verifies, and
|
|
758
|
+
`isAttestorTeeAttestationVerified` comes back `false` to say nothing was
|
|
759
|
+
attested. If
|
|
760
|
+
`proofValidation.attestorTeeAttestation.expectedImageDigests` is also set, the
|
|
761
|
+
SDK uses the intersection: caller policy can narrow the published trust list,
|
|
762
|
+
but can't broaden it. TEE settings preserve the same configuration objects;
|
|
744
763
|
`verifyResultFull` does not accept injected verification hooks. The surrounding
|
|
745
764
|
issuer, session, and context checks are stateless; persist accepted session IDs
|
|
746
765
|
in your application to prevent replay.
|
|
@@ -772,7 +791,10 @@ is therefore always verified; wrap the call to inspect failures:
|
|
|
772
791
|
```ts
|
|
773
792
|
import { ResultVerificationError } from '@reclaimprotocol/client'
|
|
774
793
|
try {
|
|
775
|
-
const { result } = await reclaim.results.receive(body, {
|
|
794
|
+
const { result } = await reclaim.results.receive(body, {
|
|
795
|
+
orgEthPrivateKey,
|
|
796
|
+
reclaimSessionId,
|
|
797
|
+
})
|
|
776
798
|
// result.payload is verified
|
|
777
799
|
} catch (err) {
|
|
778
800
|
if (err instanceof ResultVerificationError) {
|
|
@@ -802,18 +824,17 @@ loadDecryptionKey(hex): DecryptionKey // 0x-hex eth private key → r
|
|
|
802
824
|
uncompressedPubkeyToJwk(hex): Jwk // 0x04+128 hex → secp256k1 JWK
|
|
803
825
|
pubkeyJwkToEthAddress(jwk): string // secp256k1 JWK → 0x eth address
|
|
804
826
|
|
|
805
|
-
// ECIES (
|
|
827
|
+
// ECIES (Builder encrypts terminal results; consumers only decrypt)
|
|
806
828
|
encryptForEthPublicKey(publicJwk, bytes): Promise<string> // → ECIES ciphertext (base64url)
|
|
807
829
|
decryptCallback(decryptionKey, body): Promise<Jws> // ECIES/plaintext → JWS
|
|
808
830
|
|
|
809
|
-
// Result JWS (
|
|
810
|
-
signResultJws(ethPrivHex, payload, issuer): Jws // VC-side
|
|
831
|
+
// Result JWS (Builder-managed ES256K)
|
|
811
832
|
verifyResult(jws, opts): VerifyResultOutcome // offline signature/nonce check (throws if invalid)
|
|
812
|
-
verifyResultFull(jws, opts): Promise<VerifyResultFullOutcome> // + JWKS discovery + exact
|
|
833
|
+
verifyResultFull(jws, opts): Promise<VerifyResultFullOutcome> // + JWKS discovery + exact proof verification (throws if invalid)
|
|
813
834
|
verifyJwsSignature(jws, signerPublicKey), decodeJwsPayload(jws)
|
|
814
835
|
|
|
815
|
-
// JWKS (signature-key discovery
|
|
816
|
-
fetchJwks(url),
|
|
836
|
+
// JWKS (signature-key discovery)
|
|
837
|
+
fetchJwks(url), selectKey(...), jwkToUncompressedPublicKey(jwk)
|
|
817
838
|
|
|
818
839
|
// Legacy compatibility helper. verifyResultFull does not use an inner proof JWS.
|
|
819
840
|
verifyNestedProof(...): NestedProofOutcome
|