npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reclaimprotocol/js-sdk

v5.2.0

Published

Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.

Readme

Reclaim Protocol JavaScript SDK Integration Guide

This guide will walk you through integrating the Reclaim Protocol JavaScript SDK into your application. We'll create a simple React application that demonstrates how to use the SDK to generate proofs and verify claims.

Official documentation

Prerequisites

Before we begin, make sure you have:

  1. An application ID from Reclaim Protocol.
  2. An application secret from Reclaim Protocol.
  3. A provider ID for the specific service you want to verify.

You can obtain these details from the Reclaim Developer Portal.

Step 1: Create a new React application

Let's start by creating a new React application:

npx create-react-app reclaim-app
cd reclaim-app

Step 2: Install necessary dependencies

Install the Reclaim Protocol SDK and a QR code generator:

npm install @reclaimprotocol/js-sdk react-qr-code

Current SDK Version: 5.2.0

Step 3: Set up your React component

Replace the contents of src/App.js with the following code:

import React, { useState, useEffect } from "react";
import { ReclaimProofRequest, verifyProof, ClaimCreationType } from "@reclaimprotocol/js-sdk";
import QRCode from "react-qr-code";

function App() {
  const [reclaimProofRequest, setReclaimProofRequest] = useState(null);
  const [requestUrl, setRequestUrl] = useState("");
  const [statusUrl, setStatusUrl] = useState("");
  const [proofs, setProofs] = useState(null);

  useEffect(() => {
    async function initializeReclaim() {
      const APP_ID = "YOUR_APPLICATION_ID_HERE";
      const APP_SECRET = "YOUR_APPLICATION_SECRET_HERE";
      const PROVIDER_ID = "YOUR_PROVIDER_ID_HERE";

      const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID);
      setReclaimProofRequest(proofRequest);
    }

    initializeReclaim();
  }, []);

  async function handleCreateClaim() {
    if (!reclaimProofRequest) {
      console.error("Reclaim Proof Request not initialized");
      return;
    }

    const url = await reclaimProofRequest.getRequestUrl();
    setRequestUrl(url);

    const status = reclaimProofRequest.getStatusUrl();
    setStatusUrl(status);
    console.log("Status URL:", status);

    await reclaimProofRequest.startSession({
      onSuccess: (proofs) => {
        if (proofs && typeof proofs !== "string") {
          // When using the default callback url, we get a proof
          if (Array.isArray(proofs)) {
            if (proofs.length == 0) {
              // From version 4.10.1, this is the case when using a custom callback url
              // Proofs are sent to the callback url.
              console.log("No proofs received. This is expected when using a custom callback url.");
            } else {
              // when using the cascading providers, providers having more than one proof will return an array of proofs
              console.log(JSON.stringify(proofs.map((p) => p.claimData.context)));
            }
          } else {
            console.log("Proof received:", proofs?.claimData.context);
          }
          setProofs(proofs);
        }
      },
      onError: (error) => {
        console.error("Verification failed", error);
      },
    });
  }

  return (
    <div className="App">
      <h1>Reclaim Protocol Demo</h1>
      <button onClick={handleCreateClaim}>Create Claim</button>
      {requestUrl && (
        <div>
          <h2>Scan this QR code to start the verification process:</h2>
          <QRCode value={requestUrl} />
        </div>
      )}
      {proofs && (
        <div>
          <h2>Verification Successful!</h2>
          <pre>{JSON.stringify(proofs, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}

export default App;

Step 4: Understanding the code

Let's break down what's happening in this code:

  1. We initialize the Reclaim SDK with your application ID, secret, and provider ID. This happens once when the component mounts.

  2. When the user clicks the "Create Claim" button, we:

    • Generate a request URL using getRequestUrl(). This URL is used to create the QR code.
    • Get the status URL using getStatusUrl(). This URL can be used to check the status of the claim process.
    • Start a session with startSession(), which sets up callbacks for successful and failed verifications, and allows you to pass an optional verificationConfig to customize proof verification.
  3. We display a QR code using the request URL. When a user scans this code, it starts the verification process.

  4. The status URL is logged to the console. You can use this URL to check the status of the claim process programmatically.

  5. When the verification is successful, we display the proof data on the page.

Step 5: New Streamlined Flow with Browser Extension Support

The Reclaim SDK now provides a simplified triggerReclaimFlow() method that automatically handles the verification process across different platforms and devices. This method intelligently chooses the best verification method based on the user's environment.

Using triggerReclaimFlow()

Replace the handleCreateClaim function in your React component with this simpler approach:

async function handleCreateClaim() {
  if (!reclaimProofRequest) {
    console.error("Reclaim Proof Request not initialized");
    return;
  }

  try {
    // Start the verification process automatically
    await reclaimProofRequest.triggerReclaimFlow();

    // Listen for the verification results
    await reclaimProofRequest.startSession({
      onSuccess: (proofs) => {
        if (proofs && typeof proofs !== "string") {
          if (Array.isArray(proofs)) {
            if (proofs.length == 0) {
              // proofs sent to callback url
            } else {
              console.log(JSON.stringify(proofs.map((p) => p.claimData.context)));
            }
          } else {
            console.log("Proof received:", proofs?.claimData.context);
          }
          setProofs(proofs);
        }
      },
      onError: (error) => {
        console.error("Verification failed", error);
      },
    });
  } catch (error) {
    console.error("Error triggering Reclaim flow:", error);
  }
}

How triggerReclaimFlow() Works

The triggerReclaimFlow() method supports two verification modes via verificationMode:

  • 'portal' (default): Opens the portal URL for remote browser verification. Opens in a new tab by default, or embedded in an iframe when target is provided.
  • 'app': Verifier app flow via the share page. Uses App Clip on iOS if useAppClip is true.

The method returns a FlowHandle that lets you close the flow programmatically:

// Portal flow (default) — opens in new tab
const handle = await reclaimProofRequest.triggerReclaimFlow();
handle.tab;    // Window reference to the opened tab
handle.close(); // close tab and stop polling

// Embedded — portal loads inside a DOM element as an iframe
const handle = await reclaimProofRequest.triggerReclaimFlow({
  target: document.getElementById('reclaim-container')
});
handle.iframe; // HTMLIFrameElement reference
handle.iframe?.style.height = '700px'; // customize the iframe
handle.close(); // remove iframe and stop polling

// Verifier app flow
const handle = await reclaimProofRequest.triggerReclaimFlow({ verificationMode: 'app' });
handle.close(); // stop polling

On Desktop Browsers:

  1. Embedded mode: If target is provided, the portal loads in an iframe inside the target element.
  2. Browser Extension: If the Reclaim browser extension is installed and no target is set, it will use the extension.
  3. Portal mode (no extension, no target): Opens the portal in a new tab.
  4. App mode (no extension): Shows QR code modal with share page URL.

On Mobile Devices (portal mode):

Opens the portal in a new tab, or in an iframe if target is provided.

On Mobile Devices (app mode):

  1. All platforms: Redirects to the verifier app.
  2. iOS with useAppClip: true: Redirects to the Reclaim App Clip instead.

The same verificationMode option works with getRequestUrl():

// Portal URL (default)
const url = await reclaimProofRequest.getRequestUrl();

// Verifier app URL
const url = await reclaimProofRequest.getRequestUrl({ verificationMode: 'app' });

Browser Extension Support

The SDK now includes built-in support for the Reclaim browser extension, providing users with a seamless verification experience without leaving their current browser tab.

Features:

  • Automatic Detection: The SDK automatically detects if the Reclaim browser extension is installed
  • Seamless Integration: No additional setup required - the extension integration works out of the box
  • Fallback Support: If the extension is not available, the SDK gracefully falls back to QR code or mobile app flows

Manual Extension Detection:

You can also manually check if the browser extension is available:

const isExtensionAvailable = await reclaimProofRequest.isBrowserExtensionAvailable();
if (isExtensionAvailable) {
  console.log("Reclaim browser extension is installed");
} else {
  console.log("Browser extension not available, will use alternative flow");
}

Configuring Browser Extension Options:

You can customize the browser extension behavior during SDK initialization:

const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
  useBrowserExtension: true, // Enable/disable browser extension (default: true)
  extensionID: "custom-extension-id", // Use custom extension ID if needed
  // ... other options
});

Modal Customization

When the QR code modal is displayed (fallback on desktop), you can customize its appearance and behavior:

// Set modal options before triggering the flow
reclaimProofRequest.setModalOptions({
  title: "Custom Verification Title",
  description: "Scan this QR code with your mobile device to verify your account",
  darkTheme: true, // Enable dark theme (default: false)
  modalPopupTimer: 5, // Auto-close modal after 5 minutes (default: 1 minute)
  showExtensionInstallButton: true, // Show extension install button (default: false)
  extensionUrl: "https://custom-extension-url.com", // Custom extension download URL
  onClose: () => {
    console.log("Modal was closed");
  }, // Callback when modal is closed
});

await reclaimProofRequest.triggerReclaimFlow();

Benefits of the New Flow:

  1. Platform Adaptive: Automatically chooses the best verification method for each platform
  2. Embeddable: Embed the portal directly in your page via iframe with { target: element }
  3. Controllable: Returns a FlowHandle to close the flow or access the iframe at any time
  4. Extension Support: Leverages browser extension for desktop users when available
  5. Mobile Optimized: Verifier app experiences on mobile devices

Step 6: Run your application

Start your development server:

npm start

Your Reclaim SDK demo should now be running. Click the "Create Claim" button to generate a QR code. Scan this code to start the verification process.

Understanding the Claim Process

  1. Creating a Claim: When you click "Create Claim", the SDK generates a unique request for verification.

  2. QR Code: The QR code contains the request URL. When scanned, it initiates the verification process.

  3. Status URL: This URL (logged to the console) can be used to check the status of the claim process. It's useful for tracking the progress of verification.

  4. Verification: The onSuccess is called when verification is successful, providing the proof data. When using a custom callback url, the proof is returned to the callback url and we get an empty array instead of a proof.

  5. Handling Failures: The onError is called if verification fails, allowing you to handle errors gracefully.

Advanced Configuration

The Reclaim SDK offers several advanced options to customize your integration:

  1. Adding Context: You can add context to your proof request, which can be useful for providing additional information:

    reclaimProofRequest.setContext("0x00000000000", "Example context message");
    
    // deprecated method: use setContext instead
    reclaimProofRequest.addContext("0x00000000000", "Example context message");
  2. Setting Parameters: If your provider requires specific parameters, you can set them like this:

    reclaimProofRequest.setParams({ email: "[email protected]", userName: "testUser" });
  3. Custom Redirect URL:

Set a custom URL to redirect users after the verification process.

reclaimProofRequest.setRedirectUrl("https://example.com/redirect");

Redirection with body:

  • url: The URL where users should be redirected after successful proof generation.
  • method (optional): The redirection method to use. Allowed options: GET (default) and POST. Note: POST form redirection is only supported in Portal flow.
  • body (optional): List of name-value pairs to be sent as the body of the form request.
    • When method is POST, body is sent with application/x-www-form-urlencoded content type.
    • When method is GET, if body is set, it is sent as query parameters. Note: Sending body on redirection is only supported in Portal flow.
reclaimProofRequest.setRedirectUrl(
  "https://example.com/redirect",
  "POST", // Portal flow only
  [{ name: "foo", value: "bar" }]  // Portal flow only
);
  1. Custom Cancel Redirect URL: Set a custom URL to redirect users on a cancellation which aborts the verification process.

reclaimProofRequest.setCancelRedirectUrl("https://example.com/error-redirect");

Redirection with body:

  • url: The URL where users should be redirected after an error which aborts the verification process.
  • method (optional): The redirection method to use. Allowed options: GET (default) and POST. Note: POST form redirection is only supported in Portal flow.
  • body (optional): List of name-value pairs to be sent as the body of the form request.
    • When method is POST, body is sent with application/x-www-form-urlencoded content type.
    • When method is GET, if body is set, it is sent as query parameters. Note: Sending body on redirection is only supported in Portal flow.
reclaimProofRequest.setCancelRedirectUrl(
  "https://example.com/error-redirect",
  "POST",  // Portal flow only
  [{ name: "error_code", value: "1001" }]  // Portal flow only
);
  1. Custom Callback URL: Set a custom callback URL for your app which allows you to receive proofs and status updates on your callback URL:

    Note: When a custom callback URL is set, proofs are sent to the custom URL instead of the Reclaim backend. Consequently, the onSuccess callback will be invoked with an empty array ([]) instead of the proof data.

    By default, proofs are sent as HTTP POST with Content-Type as application/x-www-form-urlencoded. Pass function argument jsonProofResponse as true to send proofs with Content-Type as application/json.

    reclaimProofRequest.setAppCallbackUrl("https://example.com/callback", true);

This verification session's id will also be present in X-Reclaim-Session-Id header of the request.

The request URL will contain query param allowAiWitness with value true when AI Witness should be allowed by handler of the request.

  1. Custom Error Callback URL:

Set a custom cancel callback URL for your app which allows you to receive user- or provider-initiated cancellation on your callback URL:

reclaimProofRequest.setCancelCallbackUrl("https://example.com/error-callback");

When verificaiton is cancelled by user (or upon error when auto-submit is enabled), following data is sent as an HTTP POST request to the url with Content-Type: application/json:

{
  "type": "string", // Name of the exception
  "message": "string",
  "sessionId": "string",
  // context as canonicalized json string
  "context": "string",
  // Other fields with more details about error may be present
  // [key: any]: any
}

This verification session's id will also be present in X-Reclaim-Session-Id header of the request.

For more details about response format, check out official documentation of Error Callback URL.

  1. Modal Customization for Desktop Users: Customize the appearance and behavior of the QR code modal shown to desktop users:

    reclaimProofRequest.setModalOptions({
      title: "Verify Your Account",
      description: "Scan the QR code with your mobile device or install our browser extension",
      darkTheme: false, // Enable dark theme (default: false)
      extensionUrl: "https://chrome.google.com/webstore/detail/reclaim", // Custom extension URL
    });
  2. Browser Extension Configuration: Configure browser extension behavior and detection:

    // Check if browser extension is available
    const isExtensionAvailable = await reclaimProofRequest.isBrowserExtensionAvailable();
    
    // Trigger the verification flow with automatic platform detection
    await reclaimProofRequest.triggerReclaimFlow();
    
    // Initialize with browser extension options
    const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
      useBrowserExtension: true, // Enable browser extension support (default: true)
      extensionID: "custom-extension-id", // Custom extension identifier
      useAppClip: false, // Enable mobile app clips (default: false)
      log: true, // Enable troubleshooting mode and more verbose logging for debugging
    });
  3. Custom Portal URL and App Clip URLs: You can customize the portal/share page and app clip URLs for your app:

const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
  portalUrl: "https://your-custom-domain.com/verify", // Custom URL for both portal and app modes
  customAppClipUrl: "https://appclip.apple.com/id?p=your.custom.app.clip", // Custom iOS App Clip URL
  // ... other options
});

Defaults:

  • Portal mode: https://portal.reclaimprotocol.org — remote browser verification
  • App mode: https://share.reclaimprotocol.org — redirects to the verifier app

Setting portalUrl overrides both modes. The previous customSharePageUrl is deprecated but still supported — if both are provided, portalUrl takes precedence. useAppClip defaults to false.

  1. Platform-Specific Flow Control: Both triggerReclaimFlow() and getRequestUrl() support verificationMode:
// Portal flow (default) — opens in new tab
const handle = await reclaimProofRequest.triggerReclaimFlow();
handle.tab;    // Window reference
handle.close(); // close tab, stop polling

// Embedded portal — loads inside a DOM element
const handle = await reclaimProofRequest.triggerReclaimFlow({
  target: document.getElementById('container')
});
handle.iframe; // HTMLIFrameElement reference
handle.close(); // remove iframe, stop polling

// Verifier app flow
const handle = await reclaimProofRequest.triggerReclaimFlow({ verificationMode: 'app' });
handle.close(); // stop polling

// getRequestUrl also supports verificationMode
const portalUrl = await reclaimProofRequest.getRequestUrl();
const appUrl = await reclaimProofRequest.getRequestUrl({ verificationMode: 'app' });
  1. Exporting and Importing SDK Configuration: You can export the entire Reclaim SDK configuration as a JSON string and use it to initialize the SDK with the same configuration on a different service or backend:
// On the client-side or initial service
const configJson = reclaimProofRequest.toJsonString();
console.log("Exportable config:", configJson);

// Send this configJson to your backend or another service

// On the backend or different service
const importedRequest = ReclaimProofRequest.fromJsonString(configJson);
const requestUrl = await importedRequest.getRequestUrl();

This allows you to generate request URLs and other details from your backend or a different service while maintaining the same configuration.

  1. Utility Methods: Additional utility methods for managing your proof requests:
// Get the current session ID
const sessionId = reclaimProofRequest.getSessionId();
console.log("Current session ID:", sessionId);
  1. Control auto-submission of proofs:

Whether the verification client should automatically submit necessary proofs once they are generated. If set to false, the user must manually click a button to submit. Defaults to true.

// Initialize with options
const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
  canAutoSubmit: true,
});
  1. Add additional metadata for verification client:

Additional metadata to pass to the verification client. This can be used to customize the client experience, such as customizing themes or UI by passing context-specific information. The keys and values must be strings. For most clients, this is not required and goes unused.

This has no effect on the verification process.

// Initialize with options
const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
  metadata: { theme: 'dark', verify_another_way_link: 'https://exampe.org/alternative-verification?id=1234' },
});
  1. Set preferred locale for verification client:

An identifier used to select a user's language and formatting preferences.

This represents a Unicode Language Identifier (i.e. without Locale extensions), except variants are not supported. Locales are expected to be canonicalized according to the "preferred value" entries in the IANA Language Subtag Registry. For example, he, and iw are equal and both have the languageCode he, because iw is a deprecated language subtag that was replaced by the subtag he.

Defaults to the browser's locale if available, otherwise English (en).

// Initialize with options
const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
  preferredLocale: 'en-US',
});

Handling Proofs on Your Backend

For production applications, it's recommended to handle proofs, and cancellations on your backend:

  1. Set a callback URL:

    reclaimProofRequest.setAppCallbackUrl("https://your-backend.com/receive-proofs");
  2. Set a cancel callback URL:

    reclaimProofRequest.setCancelCallbackUrl("https://your-backend.com/receive-cancel");

These options allow you to securely process proofs or cancellations on your server.

[!TIP] Best Practice: When using setAppCallbackUrl and/or setCancelCallbackUrl, your backend receives the proof or cancellation details directly. We recommend your backend notifies the frontend (e.g. via WebSockets, SSE, or polling) to stop the verification process and handle the appropriate success/failure action. Do not rely completely on startSession callbacks on the frontend when using these backend callbacks.

Proof Verification

The SDK provides a verifyProof function to manually verify proofs cryptographically on your backend. This involves validating the attestor signatures and enforcing that the proof matches the expected requirements.

Here are the possible ways to use verifyProof, from beginner to advanced:

1. Simple Verification

The easiest way is to use request.getProviderVersion() which returns the provider ID and exact version used in the session — pass it directly as the config:

import { verifyProof } from "@reclaimprotocol/js-sdk";

// Using getProviderVersion() — recommended
const providerVersion = reclaimProofRequest.getProviderVersion();
const { isVerified, data, error } = await verifyProof(proof, providerVersion);
if (isVerified) {
  console.log("Proof is valid");
  console.log("Context:", data[0].context);
  console.log("Extracted parameters:", data[0].extractedParameters);
} else {
  console.log("Proof is invalid, reason:", error);
}

Or, by manually providing the provider details:

const { isVerified, data } = await verifyProof(proof, {
  providerId: "YOUR_PROVIDER_ID",
  // The exact provider version used in the session.
  providerVersion: "1.0.0",
  // Optionally provide tags. For example, this can be `['ai']` when you want to allow patches from ai.
  allowedTags: ["ai"]
});

Or, with a known hash:

const { isVerified, data, error } = await verifyProof(proof, { hashes: ['0xAbC...'] });

2. Intermediate: Strict Hash Verification

If you want to avoid network requests, you can manually feed the expected cryptographic hashes your system allows.

// Verify a proof against a known, strict expected hash
const { isVerified, data } = await verifyProof(proof, {
  hashes: ['0x1abc2def3456...']
});

3. Advanced: Multiple Proofs and Optional Matches

When building advanced use-cases, you might process multiple distinct proofs at once or deal with providers that yield a few valid hash possibilities (e.g., due to optional data fields).

const result = await verifyProof([proof1, proof2, sameAsProof2], {
  hashes: [
    // A string hash is equivalent to an object with { value: '...', required: true, multiple: true }.
    '0xStrictHash123...',
    {
       // An array 'value' means that 1 proof can have any 1 matching hash
       // from this list, typically because of optional variables in the original request.
       value: ['0xOptHash1...', '0xOptHashA...'],
       // 'multiple' being true (which is the default) means any proof matching this hash
       // is allowed to appear multiple times in the list of proofs you are verifying.
       multiple: true
    },
    {
       value: '0xE33...',
       // 'required: false' means there can be 0 proofs matching this hash.
       // Such proofs may be optionally present in the list of proofs.
       // (By default, 'required' is true).
       required: false
    }
  ]
});

if (result.isVerified) {
  result.data.forEach((d, i) => {
    console.log(`Proof ${i + 1} context:`, d.context);
    console.log(`Proof ${i + 1} params:`, d.extractedParameters);
  });
}

4. Danger Zone: Disabled Content Validation

If you only want to verify the attestor signature but wish to dangerously bypass the parameter/content match (Not Recommended):

const { isVerified } = await verifyProof(proof, {
  dangerouslyDisableContentValidation: true
});

TEE Attestation Verification

The SDK supports verifying TEE (Trusted Execution Environment) attestations included in proofs. The attestation flow verifies the Google Confidential Computing OIDC token returned by Popcorn's GCP attestor and checks that it is bound to the proof nonce, application identity, and image digests.

Enabling TEE Attestation

To request TEE attestation during proof generation, enable it during initialization:

const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
  acceptTeeAttestation: true,
});

Verifying TEE Attestation via verifyProof

Provide a teeAttestation config to require and verify TEE attestation. The application ID is derived automatically from appSecret. If TEE data is missing or invalid, verification will fail with a TeeVerificationError.

import { verifyProof, TeeVerificationError } from "@reclaimprotocol/js-sdk";

const { isVerified, isTeeAttestationVerified, data, error } = await verifyProof(proof, {
  hashes: ['0xAbC...'],
  teeAttestation: {
    appSecret: APP_SECRET,
  },
});

if (isVerified) {
  console.log("Proof verified with hardware attestation");
  console.log("TEE verified:", isTeeAttestationVerified); // always true when teeAttestation is provided and passes
  console.log("Extracted parameters:", data[0].extractedParameters);
} else if (error instanceof TeeVerificationError) {
  console.log("TEE verification failed:", error.message);
} else {
  console.log("Proof verification failed:", error);
}

The result includes isTeeAttestationVerified. It is true when teeAttestation was provided and verification passed, and undefined when teeAttestation was not requested.

What TEE verification checks

  • Application binding: Derives the application ID from appSecret and confirms the attestation was generated for your application
  • Nonce binding: Ensures the attestation nonce matches the proof context
  • Session and timestamp binding: Verifies the nonce metadata matches the proof session and is within the allowed skew
  • OIDC token signature: Validates the Google Confidential Computing attestation JWT against Google's JWKS (responses are cached for 5 minutes)
  • Platform claims: Confirms the expected GCP confidential-computing claims (issuer, secure boot, hardware model, GCE instance metadata)
  • Digest binding: Confirms the workload and verifier image digests are present in the attestation token nonce list

Standalone verifyTeeAttestation

You can verify TEE attestation for a single proof without running full proof verification:

import { verifyTeeAttestation } from "@reclaimprotocol/js-sdk";

const { isVerified, error } = await verifyTeeAttestation(proof, APP_SECRET);
if (isVerified) {
  console.log("TEE attestation verified");
} else {
  console.log("TEE verification failed:", error);
}

appSecret is your Reclaim application secret. The application ID is derived from it automatically.

Browser vs Server Verification

TEE verification fetches Google's OIDC metadata and JWKS endpoints. In browser-only environments this may fail due to cross-origin restrictions.

For web apps, verify TEE attestations on the server:

const response = await fetch('/api/verify-tee', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ proofs }),
});

const { results } = await response.json();
console.log(results);

The server route should read the app secret from environment variables only (never accept it from the client request body). See the example app for a reference implementation:

  • example/src/app/api/verify-tee/route.ts

Error Handling

The SDK provides specific error types for different failure scenarios. Here's how to handle them:

import { ReclaimProofRequest } from "@reclaimprotocol/js-sdk";

try {
  const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID);

  await proofRequest.startSession({
    onSuccess: (proofs) => {
      // proofs can be empty if callback url set
      console.log("Proof received:", proofs);
    },
    onError: (error) => {
      // Handle different error types
      if (error.name === "ProofNotVerifiedError") {
        console.error("Proof verification failed");
      } else if (error.name === "ProviderFailedError") {
        console.error("Provider failed to generate proof");
      } else if (error.name === "SessionNotStartedError") {
        console.error("Session could not be started");
      } else {
        console.error("Unknown error:", error.message);
      }
    },
  });
} catch (error) {
  // Handle initialization errors
  if (error.name === "InitError") {
    console.error("Failed to initialize SDK:", error.message);
  } else if (error.name === "InvalidParamError") {
    console.error("Invalid parameters provided:", error.message);
  }
}

Common Error Types:

  • InitError: SDK initialization failed
  • InvalidParamError: Invalid parameters provided
  • SignatureNotFoundError: Missing or invalid signature
  • ProofNotVerifiedError: Proof verification failed
  • ProviderFailedError: Provider failed to generate proof
  • SessionNotStartedError: Session could not be started
  • ProofSubmissionFailedError: Proof submission to callback failed
  • ErrorDuringVerificationError: An abort error during verification which was caused by the user aborting the verification process or provider's JS script raising a validation error

Example Repos

Next Steps

Explore the Reclaim Protocol documentation for more advanced features and best practices for integrating the SDK into your production applications.

Happy coding with Reclaim Protocol!

Contributing to Our Project

We welcome contributions to our project! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.

Security Note

Always keep your Application Secret secure. Never expose it in client-side code or public repositories.

Code of Conduct

Please read and follow our Code of Conduct to ensure a positive and inclusive environment for all contributors.

Security

If you discover any security-related issues, please refer to our Security Policy for information on how to responsibly disclose vulnerabilities.

Contributor License Agreement

Before contributing to this project, please read and sign our Contributor License Agreement (CLA).

Indie Hackers

For Indie Hackers: Check out our guidelines and potential grant opportunities

License

This project is licensed under a custom license. By contributing to this project, you agree that your contributions will be licensed under its terms.

Thank you for your contributions!