From f53064b05c579a0f7ab262fa2e6d93a6ffb9f12a Mon Sep 17 00:00:00 2001 From: "promptless[bot]" <179508745+promptless[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 11:24:24 +0000 Subject: [PATCH 1/2] Add 'How verification works' section to JS SDK proof verification docs --- CHANGELOG.md | 4 ++++ content/docs/js-sdk/verifying-proofs.mdx | 30 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f09a44..08aeadc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.1 + +* Added "How verification works" section to JS SDK proof verification documentation explaining signature verification and content validation + ## 0.2.0 * Added Analytics Dashboard documentation for the Developer Portal, including time range filters and OS-based device breakdown diff --git a/content/docs/js-sdk/verifying-proofs.mdx b/content/docs/js-sdk/verifying-proofs.mdx index f40989f..caa88ea 100644 --- a/content/docs/js-sdk/verifying-proofs.mdx +++ b/content/docs/js-sdk/verifying-proofs.mdx @@ -8,6 +8,36 @@ The [proof generation](/js-sdk/generating-proof) is a client side operation. On Verifying proofs is a very simple light weight operation. You can think of it as verifying the digital signatures to make sure the data is tamper resistant. +## How verification works + +When you call `verifyProof`, the SDK runs two checks to make sure the proof is authentic and matches your expected configuration. + +### Signature verification + +First, the SDK verifies that the proof was signed by a valid Reclaim attestor: + +1. Fetches the current list of valid attestor addresses from Reclaim's servers +2. Recovers the Ethereum addresses that signed the proof using ECDSA signature recovery +3. Confirms at least one signer matches a known valid attestor + +This ensures the proof came from a legitimate Reclaim session and wasn't fabricated. + +### Content validation + +Next, the SDK checks that the proof's content matches your provider configuration: + +1. Extracts the HTTP provider parameters from the proof (URL, method, body, response matches, and redactions) +2. Computes a keccak256 hash from these parameters using canonical JSON serialization +3. Compares this hash against the expected hashes from your provider configuration + +This prevents proof reuse attacks—where someone submits a valid proof from a different provider or configuration. + +### Under the hood + +The verification uses ECDSA signature recovery via ethers.js `verifyMessage()` and keccak256 hashing with canonical JSON serialization to ensure consistent results across platforms. + +Both checks must pass for `verifyProof` to return `true`. If either fails, it returns `false` (or throws a `ProofNotVerifiedError` if you're using `assertValidProof`). + # Quickstart ## Setup the callback endpoint This should be the endpoint that will be called by your [`onSuccess` callback](generating-proof). From 0868087fda6d91f2b5ee75e817499bcca4e690fc Mon Sep 17 00:00:00 2001 From: "promptless[bot]" Date: Fri, 17 Apr 2026 22:05:26 +0000 Subject: [PATCH 2/2] Resolve base-branch merge conflicts --- content/docs/js-sdk/usage.mdx | 31 ++++ content/docs/js-sdk/verifying-proofs.mdx | 196 ----------------------- 2 files changed, 31 insertions(+), 196 deletions(-) delete mode 100644 content/docs/js-sdk/verifying-proofs.mdx diff --git a/content/docs/js-sdk/usage.mdx b/content/docs/js-sdk/usage.mdx index c4e570f..1f11bdf 100644 --- a/content/docs/js-sdk/usage.mdx +++ b/content/docs/js-sdk/usage.mdx @@ -259,6 +259,37 @@ const appUrl = await reclaimProofRequest.getRequestUrl({ verificationMode: 'app' ## Part 2 : Process Verification endpoint > Be sure to expose this endpoint over the public internet. If testing locally, use [Ngrok](https://ngrok.dev) + +### How verification works + +When you call `verifyProof`, the SDK runs two checks to make sure the proof is authentic and matches your expected configuration. + +#### Signature verification + +First, the SDK verifies that the proof was signed by a valid Reclaim attestor: + +1. Fetches the current list of valid attestor addresses from Reclaim's servers +2. Recovers the Ethereum addresses that signed the proof using ECDSA signature recovery +3. Confirms at least one signer matches a known valid attestor + +This ensures the proof came from a legitimate Reclaim session and wasn't fabricated. + +#### Content validation + +Next, the SDK checks that the proof's content matches your provider configuration: + +1. Extracts the HTTP provider parameters from the proof (URL, method, body, response matches, and redactions) +2. Computes a keccak256 hash from these parameters using canonical JSON serialization +3. Compares this hash against the expected hashes from your provider configuration + +This prevents proof reuse attacks—where someone submits a valid proof from a different provider or configuration. + +#### Under the hood + +The verification uses ECDSA signature recovery via ethers.js `verifyMessage()` and keccak256 hashing with canonical JSON serialization to ensure consistent results across platforms. + +Both checks must pass for `verifyProof` to return `true`. If either fails, it returns `false` (or throws a `TeeVerificationError` if using TEE attestation verification). + ### Verify Proofs ```javascript import { verifyProof } from '@reclaimprotocol/js-sdk'; diff --git a/content/docs/js-sdk/verifying-proofs.mdx b/content/docs/js-sdk/verifying-proofs.mdx deleted file mode 100644 index caa88ea..0000000 --- a/content/docs/js-sdk/verifying-proofs.mdx +++ /dev/null @@ -1,196 +0,0 @@ ---- -title: Verifying the proof -description: Critical step to make sure user is not trying to defraud your app ---- - -# Why do I need to verify, when the proof has already been generated? -The [proof generation](/js-sdk/generating-proof) is a client side operation. On Success Callback is only responsible for notifying when the proof is generated successfully. All the proofs generated should be verified on your backend as a malicious user can bypass any check you add on the frontend. - -Verifying proofs is a very simple light weight operation. You can think of it as verifying the digital signatures to make sure the data is tamper resistant. - -## How verification works - -When you call `verifyProof`, the SDK runs two checks to make sure the proof is authentic and matches your expected configuration. - -### Signature verification - -First, the SDK verifies that the proof was signed by a valid Reclaim attestor: - -1. Fetches the current list of valid attestor addresses from Reclaim's servers -2. Recovers the Ethereum addresses that signed the proof using ECDSA signature recovery -3. Confirms at least one signer matches a known valid attestor - -This ensures the proof came from a legitimate Reclaim session and wasn't fabricated. - -### Content validation - -Next, the SDK checks that the proof's content matches your provider configuration: - -1. Extracts the HTTP provider parameters from the proof (URL, method, body, response matches, and redactions) -2. Computes a keccak256 hash from these parameters using canonical JSON serialization -3. Compares this hash against the expected hashes from your provider configuration - -This prevents proof reuse attacks—where someone submits a valid proof from a different provider or configuration. - -### Under the hood - -The verification uses ECDSA signature recovery via ethers.js `verifyMessage()` and keccak256 hashing with canonical JSON serialization to ensure consistent results across platforms. - -Both checks must pass for `verifyProof` to return `true`. If either fails, it returns `false` (or throws a `ProofNotVerifiedError` if you're using `assertValidProof`). - -# Quickstart -## Setup the callback endpoint -This should be the endpoint that will be called by your [`onSuccess` callback](generating-proof). - -This endpoint should be of type `POST`. - -## Decode the proof object -```javascript -const proofs = JSON.parse(decodedBody); -``` - -## Verify the proof -```javascript -import { verifyProof } from '@reclaimprotocol/js-sdk'; - -... -const isValid = await verifyProof(proofs); -``` - -## Extract the data -### Context -If you set contextAddress and contextMessage when building the request, you can get them back : -```javascript -const {contextAddress, contextMessage} = JSON.parse(proofs[i].context); -``` -### Extacted Parameters -The data that was extracted from the webpage that the user logged in into, depending on what data was configured to be extracted using the provider you had set when building the request. -``` -const { extractedParameters } = JSON.parse(proofs[i].context); -``` - -## Sample implementation - -### Example in Next.js -``` -import { NextResponse } from 'next/server'; -import { verifyProof } from '@reclaimprotocol/js-sdk'; - -export async function POST(request) { - let providerId = 'example'; - - try { - // assuming that you set callback url with json set to true - const proofs = JSON.parse(await request.text()); - const isValid = await verifyProof(proofs); - if (!isValid) { - return NextResponse.json({ - success: false, - message: 'Proof verification failed' - }); - } - - // As best practice, you MUST validate proofs as per expectations and business requirements. - // This should happen on your backend. - // - // This must be done to make sure that - // this is the proof you expected. - // - // As an example, validation can be done by checking request url, headers, - // method, and all proven fields (aka extracted params), etc. - const isProofValid = await YourBackendUsingReclaim.validateProof(proofs); - - if (!isProofValid) { - // Do not use proof that failed your validation. - return NextResponse.json({ - success: false, - message: 'Proof validation failed' - }); - } - - // save or use proof from your backend - - return NextResponse.json({ - success: true, - message: 'Proof verification successful' - }); - } catch (e) { - return NextResponse.json({ - success: false, - message: 'Proof verification failed' - }); - } -} -``` - -## assertValidProof - -If you prefer try/catch over checking return values, use `assertValidProof`. It throws a `ProofNotVerifiedError` when the proof is invalid. - -```javascript -import { assertValidProof, ProofNotVerifiedError } from '@reclaimprotocol/js-sdk'; - -try { - await assertValidProof(proofs); - // Proof is valid - continue with your logic -} catch (error) { - if (error instanceof ProofNotVerifiedError) { - // Handle invalid proof - console.error('Proof verification failed:', error.message); - } - throw error; -} -``` - -Both `verifyProof` and `assertValidProof` accept the same parameters: -- `proofs` - A single proof object or array of proofs -- `allowAiWitness` (optional) - Set to `true` to allow AI-generated proofs - -# Structure of proofs -```json -{ - - "identifier": "...", - - "claimData": { - - "provider": "...", - - "parameters": "...", - - "owner": "...", - - "timestampS": "...", - - "context": "{\n \"contextAddress\": \"...\",\n \"contextMessage\": \"...\",\n \"extractedParameters\": {\n \"pageTitle\": \"\",\n \"ianaLinkUrl\": \"\"\n },\n \"providerHash\": \"...\"\n}", - - "identifier": "...", - - "epoch": "..." - - }, - - "signatures": [ - - "..." - - ], - - "witnesses": [ - - { - - "id": "...", - - "url": "..." - - } - - ], - - "taskId": "'...' || null", - - "publicData": "{...} || null" - - } -``` \ No newline at end of file