Skip to content

Commit 06b6450

Browse files
authored
Merge pull request #91 from reclaimprotocol/feat/verify-result-portal
Feat/verify result portal
2 parents 728cdaa + 981fd3b commit 06b6450

11 files changed

Lines changed: 753 additions & 266 deletions

File tree

README.md

Lines changed: 133 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function App() {
9999
setProofs(proofs);
100100
}
101101
},
102-
onFailure: (error) => {
102+
onError: (error) => {
103103
console.error("Verification failed", error);
104104
},
105105
});
@@ -181,7 +181,7 @@ async function handleCreateClaim() {
181181
setProofs(proofs);
182182
}
183183
},
184-
onFailure: (error) => {
184+
onError: (error) => {
185185
console.error("Verification failed", error);
186186
},
187187
});
@@ -193,17 +193,43 @@ async function handleCreateClaim() {
193193
194194
### How triggerReclaimFlow() Works
195195
196-
The `triggerReclaimFlow()` method automatically detects the user's environment and chooses the optimal verification method:
196+
The `triggerReclaimFlow()` method supports two verification modes via `verificationMode`:
197+
198+
- **`'portal'` (default)**: Opens the portal URL for remote browser verification on all platforms.
199+
- **`'app'`**: Native app flow via the share page. Uses App Clip on iOS if `useAppClip` is `true`.
200+
201+
```javascript
202+
// Portal flow (default)
203+
await reclaimProofRequest.triggerReclaimFlow();
204+
205+
// Native app flow
206+
await reclaimProofRequest.triggerReclaimFlow({ verificationMode: 'app' });
207+
```
197208
198209
#### On Desktop Browsers:
199210
200-
1. **Browser Extension First**: If the Reclaim browser extension is installed, it will use the extension for a seamless in-browser verification experience.
201-
2. **QR Code Fallback**: If the extension is not available, it automatically displays a QR code modal for mobile scanning.
211+
1. **Browser Extension First**: If the Reclaim browser extension is installed, it will use the extension regardless of verification mode.
212+
2. **Portal mode** (no extension): Opens the portal in a new tab.
213+
3. **App mode** (no extension): Shows QR code modal with share page URL.
214+
215+
#### On Mobile Devices (portal mode):
202216
203-
#### On Mobile Devices:
217+
Opens the portal in a new tab.
204218
205-
1. **iOS Devices**: Automatically redirects to the Reclaim App Clip for native iOS verification.
206-
2. **Android Devices**: Automatically redirects to the Reclaim Instant App for native Android verification.
219+
#### On Mobile Devices (app mode):
220+
221+
1. **All platforms**: Redirects to the share page.
222+
2. **iOS with `useAppClip: true`**: Redirects to the Reclaim App Clip instead.
223+
224+
The same `verificationMode` option works with `getRequestUrl()`:
225+
226+
```javascript
227+
// Portal URL (default)
228+
const url = await reclaimProofRequest.getRequestUrl();
229+
230+
// Native app URL
231+
const url = await reclaimProofRequest.getRequestUrl({ verificationMode: 'app' });
232+
```
207233
208234
### Browser Extension Support
209235
@@ -289,7 +315,7 @@ Your Reclaim SDK demo should now be running. Click the "Create Claim" button to
289315
290316
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.
291317
292-
5. **Handling Failures**: The `onFailure` is called if verification fails, allowing you to handle errors gracefully.
318+
5. **Handling Failures**: The `onError` is called if verification fails, allowing you to handle errors gracefully.
293319
294320
## Advanced Configuration
295321
@@ -431,33 +457,35 @@ For more details about response format, check out [official documentation of Err
431457
const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
432458
useBrowserExtension: true, // Enable browser extension support (default: true)
433459
extensionID: "custom-extension-id", // Custom extension identifier
434-
useAppClip: true, // Enable mobile app clips (default: true)
460+
useAppClip: false, // Enable mobile app clips (default: false)
435461
log: true, // Enable troubleshooting mode and more verbose logging for debugging
436462
});
437463
```
438464

439-
9. **Custom Share Page and App Clip URLs**:
440-
You can customize the share page and app clip URLs for your app:
465+
9. **Custom Portal URL and App Clip URLs**:
466+
You can customize the portal/share page and app clip URLs for your app:
441467

442468
```javascript
443469
const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
444-
customSharePageUrl: "https://your-custom-domain.com/verify", // Custom share page URL
470+
portalUrl: "https://your-custom-domain.com/verify", // Custom portal URL
445471
customAppClipUrl: "https://appclip.apple.com/id?p=your.custom.app.clip", // Custom iOS App Clip URL
446472
// ... other options
447473
});
448474
```
449475

476+
> **Note:** `portalUrl` defaults to `https://portal.reclaimprotocol.org` and `useAppClip` defaults to `false` when no options are provided. `portalUrl` is the preferred option. The previous `customSharePageUrl` is deprecated but still supported. If both are provided, `portalUrl` takes precedence.
477+
450478
10. **Platform-Specific Flow Control**:
451-
The `triggerReclaimFlow()` method provides intelligent platform detection, but you can still use traditional methods for custom flows:
479+
Both `triggerReclaimFlow()` and `getRequestUrl()` support `verificationMode`:
452480

453481
```javascript
454-
// Traditional approach with manual QR code handling
455-
const requestUrl = await reclaimProofRequest.getRequestUrl();
456-
// Display your own QR code implementation
457-
458-
// Or use the new streamlined approach
482+
// Portal flow (default) — remote browser verification
459483
await reclaimProofRequest.triggerReclaimFlow();
460-
// Automatically handles platform detection and optimal user experience
484+
const portalUrl = await reclaimProofRequest.getRequestUrl();
485+
486+
// Native app flow
487+
await reclaimProofRequest.triggerReclaimFlow({ verificationMode: 'app' });
488+
const appUrl = await reclaimProofRequest.getRequestUrl({ verificationMode: 'app' });
461489
```
462490

463491
11. **Exporting and Importing SDK Configuration**:
@@ -560,19 +588,20 @@ The easiest way is to let the SDK retrieve the requirements automatically. If yo
560588
```javascript
561589
import { verifyProof } from "@reclaimprotocol/js-sdk";
562590
563-
// Fast and simple automatically fetched verification
564-
const isValid = await verifyProof(proof, request.getProviderVersion());
565-
566-
if (isValid) {
591+
// Verify a single proof
592+
const { isVerified, data } = await verifyProof(proof, { hashes: ['0xAbC...'] });
593+
if (isVerified) {
567594
console.log("Proof is valid");
595+
console.log("Context:", data[0].context);
596+
console.log("Extracted parameters:", data[0].extractedParameters);
568597
} else {
569598
console.log("Proof is invalid");
570599
}
571600
```
572601
573602
Or, by manually providing the details:
574603
```javascript
575-
const isValid = await verifyProof(proof, {
604+
const { isVerified, data } = await verifyProof(proof, {
576605
providerId: "YOUR_PROVIDER_ID",
577606
// The exact provider version used in the session.
578607
providerVersion: "1.0.0",
@@ -587,47 +616,106 @@ If you want to avoid network requests, you can manually feed the expected crypto
587616
588617
```javascript
589618
// Verify a proof against a known, strict expected hash
590-
const isValid = await verifyProof(proof, {
591-
hashes: ['0x1abc2def3456...']
619+
const { isVerified, data } = await verifyProof(proof, {
620+
hashes: ['0x1abc2def3456...']
592621
});
593622
```
594623
595624
### 3. Advanced: Multiple Proofs and Optional Matches
596625
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).
597626
598627
```javascript
599-
const areAllValid = await verifyProof([proof1, proof2, sameAsProof2], {
628+
const result = await verifyProof([proof1, proof2, sameAsProof2], {
600629
hashes: [
601630
// A string hash is equivalent to an object with { value: '...', required: true, multiple: true }.
602-
'0xStrictHash123...',
603-
{
604-
// An array 'value' means that 1 proof can have any 1 matching hash
631+
'0xStrictHash123...',
632+
{
633+
// An array 'value' means that 1 proof can have any 1 matching hash
605634
// from this list, typically because of optional variables in the original request.
606-
value: ['0xOptHash1...', '0xOptHashA...'],
607-
// 'multiple' being true (which is the default) means any proof matching this hash
635+
value: ['0xOptHash1...', '0xOptHashA...'],
636+
// 'multiple' being true (which is the default) means any proof matching this hash
608637
// is allowed to appear multiple times in the list of proofs you are verifying.
609-
multiple: true
610-
},
611-
{
612-
value: '0xE33...',
613-
// 'required: false' means there can be 0 proofs matching this hash.
638+
multiple: true
639+
},
640+
{
641+
value: '0xE33...',
642+
// 'required: false' means there can be 0 proofs matching this hash.
614643
// Such proofs may be optionally present in the list of proofs.
615644
// (By default, 'required' is true).
616-
required: false
645+
required: false
617646
}
618647
]
619648
});
649+
650+
if (result.isVerified) {
651+
result.data.forEach((d, i) => {
652+
console.log(`Proof ${i + 1} context:`, d.context);
653+
console.log(`Proof ${i + 1} params:`, d.extractedParameters);
654+
});
655+
}
620656
```
621657
622658
### 4. Danger Zone: Disabled Content Validation
623659
If you only want to verify the attestor signature but wish to dangerously bypass the parameter/content match (Not Recommended):
624660
625661
```javascript
626-
const isValidSignature = await verifyProof(proof, {
627-
dangerouslyDisableContentValidation: true
662+
const { isVerified } = await verifyProof(proof, {
663+
dangerouslyDisableContentValidation: true
628664
});
629665
```
630666
667+
## TEE Attestation Verification
668+
669+
The SDK supports verifying TEE (Trusted Execution Environment) attestations included in proofs. This provides hardware-level assurance that the proof was generated inside a secure enclave (AMD SEV-SNP).
670+
671+
### Enabling TEE Attestation
672+
673+
To request TEE attestation during proof generation, enable it during initialization:
674+
675+
```javascript
676+
const proofRequest = await ReclaimProofRequest.init(APP_ID, APP_SECRET, PROVIDER_ID, {
677+
acceptTeeAttestation: true,
678+
});
679+
```
680+
681+
### Verifying TEE Attestation
682+
683+
Pass `true` as the third argument (`verifyTEE`) to `verifyProof` to require and verify TEE attestation. If TEE data is missing or invalid, verification will fail.
684+
685+
```javascript
686+
import { verifyProof } from "@reclaimprotocol/js-sdk";
687+
688+
// Pass true as the third argument (verifyTEE) to require TEE verification
689+
const { isVerified, isTeeVerified, data } = await verifyProof(proof, { hashes: ['0xAbC...'] }, true);
690+
691+
if (isVerified) {
692+
console.log("Proof is fully verified with hardware attestation");
693+
console.log("TEE verified:", isTeeVerified);
694+
console.log("Extracted parameters:", data[0].extractedParameters);
695+
}
696+
```
697+
698+
When `verifyTEE` is `true`, the result includes `isTeeVerified`. The overall `isVerified` will be `false` if TEE data is missing or TEE verification fails.
699+
700+
The TEE verification validates:
701+
- **Nonce binding**: Ensures the attestation nonce matches the proof context
702+
- **Application ID**: Confirms the attestation was generated for your application (optional)
703+
- **Timestamp**: Verifies the attestation timestamp is within an acceptable range of the proof timestamp
704+
- **SNP report**: Validates the AMD SEV-SNP hardware attestation report
705+
- **VLEK certificate**: Verifies the certificate chain against AMD's root of trust
706+
- **Report data**: Confirms the workload and verifier digests match the attestation
707+
708+
You can also verify TEE attestation separately using the lower-level `verifyTeeAttestation` function:
709+
710+
```javascript
711+
import { verifyTeeAttestation } from "@reclaimprotocol/js-sdk";
712+
713+
const isTeeValid = await verifyTeeAttestation(proof, APP_ID);
714+
if (isTeeValid) {
715+
console.log("TEE attestation verified — proof was generated in a secure enclave");
716+
}
717+
```
718+
631719
## Error Handling
632720

633721
The SDK provides specific error types for different failure scenarios. Here's how to handle them:
@@ -677,6 +765,10 @@ try {
677765
- `ProofSubmissionFailedError`: Proof submission to callback failed
678766
- `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
679767

768+
## Example Repos
769+
770+
- [Reclaim Demo Website](https://github.com/reclaimprotocol/reclaim-demo-website-v3)
771+
680772
## Next Steps
681773

682774
Explore the [Reclaim Protocol documentation](https://docs.reclaimprotocol.org/) for more advanced features and best practices for integrating the SDK into your production applications.

0 commit comments

Comments
 (0)