@reclaimprotocol/js-sdk 5.3.0-dev.1 → 5.4.0
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 +46 -0
- package/dist/index.d.ts +70 -1
- package/dist/index.js +174 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -701,6 +701,52 @@ const { isVerified } = await verifyProof(proof, {
|
|
|
701
701
|
});
|
|
702
702
|
```
|
|
703
703
|
|
|
704
|
+
### Replay Protection
|
|
705
|
+
|
|
706
|
+
Proofs are submitted by the user, so the server must not trust any field on the proof in isolation. `verifyProof` (and `verifyTeeAttestation`) cryptographically bind a proof to a specific session, but **the SDK is stateless** — it cannot tell whether the same valid proof has already been verified. Preventing replay is the caller's responsibility.
|
|
707
|
+
|
|
708
|
+
**What the SDK guarantees**
|
|
709
|
+
|
|
710
|
+
The `attestationNonce` baked into the proof context is computed as `keccak256("RECLAIM_TEE_NONCE_V1" : applicationId : sessionId : timestamp : appSecret)`. When you call `verifyProof` with `teeAttestation: { appSecret }`, the SDK:
|
|
711
|
+
|
|
712
|
+
- Recomputes the nonce from `(applicationId, sessionId, timestamp)` in the proof context using **your** `appSecret` and asserts it matches — only the holder of the app secret can mint a valid nonce, so the user cannot rebind a proof to a different `sessionId` or `applicationId`.
|
|
713
|
+
- Confirms the recomputed nonce appears in the TEE attestation token's `eat_nonce` (signed by Google's Confidential Computing OIDC issuer), so the TEE-signed binding cannot be forged either.
|
|
714
|
+
- Asserts the `applicationId` in the nonce data matches the address derived from `appSecret`, and that `sessionId` in the nonce data agrees with `reclaimSessionId` in `claimData.context` and `proxySessionId`/`sessionId` in `claimData.parameters`.
|
|
715
|
+
- Asserts `claimData.timestampS` is within 10 minutes of the nonce timestamp.
|
|
716
|
+
|
|
717
|
+
In short: the user cannot mutate `sessionId`, `applicationId`, or `timestamp` on a proof without invalidating it. They *can*, however, resubmit the same valid proof.
|
|
718
|
+
|
|
719
|
+
**What you must do on your server**
|
|
720
|
+
|
|
721
|
+
1. **Use a session you initiated.** Call `ReclaimProofRequest.init(...)` and `getSessionId()` server-side (or record the `sessionId` returned to your callback). When verifying, reject any proof whose `sessionId` you did not issue.
|
|
722
|
+
2. **Dedupe by `sessionId`.** Persist accepted `sessionId`s (e.g., a unique index in your DB or a TTL cache) and reject a `sessionId` that has already been verified. This is the primary replay defense.
|
|
723
|
+
3. **Require `teeAttestation`** in `verifyProof` for any flow where replay or tampering matters — without it, the cryptographic binding to `appSecret` is not checked.
|
|
724
|
+
4. **Enforce a freshness window.** Reject proofs whose `claimData.timestampS` is older than your business policy allows (the SDK only enforces a 10-minute skew between the nonce timestamp and claim timestamp, not absolute freshness).
|
|
725
|
+
|
|
726
|
+
```javascript
|
|
727
|
+
// Pseudocode for a server-side verification handler
|
|
728
|
+
const expectedSessionId = await loadSessionForUser(req.user); // session you created
|
|
729
|
+
const sessionId = JSON.parse(proof.claimData.context).reclaimSessionId;
|
|
730
|
+
|
|
731
|
+
if (sessionId !== expectedSessionId) {
|
|
732
|
+
throw new Error("session mismatch");
|
|
733
|
+
}
|
|
734
|
+
if (await db.consumedSessions.has(sessionId)) {
|
|
735
|
+
throw new Error("proof already used"); // replay
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const { isVerified, error } = await verifyProof(proof, {
|
|
739
|
+
...providerVersion,
|
|
740
|
+
teeAttestation: { appSecret: process.env.APP_SECRET },
|
|
741
|
+
});
|
|
742
|
+
if (!isVerified) throw error;
|
|
743
|
+
|
|
744
|
+
await db.consumedSessions.add(sessionId); // mark consumed atomically
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
> [!WARNING]
|
|
748
|
+
> Without server-side session tracking and dedup, a malicious user can submit the same valid proof repeatedly. The cryptographic checks in `verifyProof` are necessary but not sufficient for replay protection.
|
|
749
|
+
|
|
704
750
|
## TEE Attestation Verification
|
|
705
751
|
|
|
706
752
|
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.
|
package/dist/index.d.ts
CHANGED
|
@@ -772,6 +772,13 @@ type ProviderHashRequirementsResponse = {
|
|
|
772
772
|
* * `getProviderHashRequirementsFromSpec()` - To get the expected proof hash requirements from a provider spec.
|
|
773
773
|
* * All 3 functions above are alternatives of each other and result from these functions can be directly used as `config` parameter in this function for proof validation.
|
|
774
774
|
*
|
|
775
|
+
* Replay protection: this function is stateless. It verifies that a proof is cryptographically
|
|
776
|
+
* bound to a specific `sessionId`/`applicationId` (via the `appSecret`-keyed attestation nonce
|
|
777
|
+
* when `teeAttestation` is provided) but does not track which proofs have already been
|
|
778
|
+
* verified. Callers must (a) verify the proof's `sessionId` matches a session they initiated
|
|
779
|
+
* and (b) persist accepted `sessionId`s and reject duplicates. See the README's
|
|
780
|
+
* "Replay Protection" section for details.
|
|
781
|
+
*
|
|
775
782
|
* @param proofOrProofs - A single proof object or an array of proof objects to be verified.
|
|
776
783
|
* @param config - Verification configuration that specifies required hashes, allowed extra hashes, or disables content validation. Optionally includes `teeAttestation` to require TEE attestation verification.
|
|
777
784
|
* @returns Verification result with `isVerified`, `isTeeAttestationVerified` (always boolean), extracted `data` from each proof, and optional `error` on failure. The application ID is derived from `appSecret` automatically.
|
|
@@ -892,6 +899,47 @@ declare class ReclaimProofRequest {
|
|
|
892
899
|
* ```
|
|
893
900
|
*/
|
|
894
901
|
static init(applicationId: string, appSecret: string, providerId: string, options?: ProofRequestOptions): Promise<ReclaimProofRequest>;
|
|
902
|
+
/**
|
|
903
|
+
* Initializes a new Reclaim proof request using a signature computed externally
|
|
904
|
+
* (e.g. on a trusted backend), so `appSecret` never has to live on the client.
|
|
905
|
+
*
|
|
906
|
+
* The signature must be produced over `canonicalize({ providerId, timestamp })`
|
|
907
|
+
* using the application's `appSecret` — see `generateInitSignature()` for the
|
|
908
|
+
* exact algorithm. The same `timestamp` used at signing time must be passed here.
|
|
909
|
+
*
|
|
910
|
+
* TEE attestation: the attestation nonce depends on `sessionId`, which is only
|
|
911
|
+
* known after the backend init call. To use TEE without exposing `appSecret`,
|
|
912
|
+
* pass an async `getAttestationNonce` callback that derives the nonce on your
|
|
913
|
+
* server using `generateAttestationNonce(appSecret, applicationId, sessionId, timestamp)`.
|
|
914
|
+
* If `acceptTeeAttestation` is left enabled but no callback is provided, init throws.
|
|
915
|
+
*
|
|
916
|
+
* @param applicationId - Your Reclaim application ID
|
|
917
|
+
* @param providerId - The ID of the provider to use for proof generation
|
|
918
|
+
* @param sessionAuth - Pre-computed signature, the timestamp it was signed over,
|
|
919
|
+
* and an optional async callback to compute the attestation nonce.
|
|
920
|
+
* @param options - Optional configuration options for the proof request
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* ```typescript
|
|
924
|
+
* // Backend (Node):
|
|
925
|
+
* const timestamp = Date.now().toString();
|
|
926
|
+
* const signature = await generateInitSignature(APP_SECRET, providerId, timestamp);
|
|
927
|
+
* // ...return { signature, timestamp } to the client...
|
|
928
|
+
*
|
|
929
|
+
* // Client:
|
|
930
|
+
* const proofRequest = await ReclaimProofRequest.initWithSignature(
|
|
931
|
+
* applicationId,
|
|
932
|
+
* providerId,
|
|
933
|
+
* { signature, timestamp },
|
|
934
|
+
* { acceptTeeAttestation: false }
|
|
935
|
+
* );
|
|
936
|
+
* ```
|
|
937
|
+
*/
|
|
938
|
+
static initWithSignature(applicationId: string, providerId: string, sessionAuth: {
|
|
939
|
+
signature: string;
|
|
940
|
+
timestamp: string;
|
|
941
|
+
getAttestationNonce?: (sessionId: string) => Promise<string> | string;
|
|
942
|
+
}, options?: ProofRequestOptions): Promise<ReclaimProofRequest>;
|
|
895
943
|
/**
|
|
896
944
|
* Creates a ReclaimProofRequest instance from a JSON string representation
|
|
897
945
|
*
|
|
@@ -1171,6 +1219,7 @@ declare class ReclaimProofRequest {
|
|
|
1171
1219
|
* ```
|
|
1172
1220
|
*/
|
|
1173
1221
|
getSessionId(): string;
|
|
1222
|
+
private static validateInitOptions;
|
|
1174
1223
|
private setSignature;
|
|
1175
1224
|
private generateSignature;
|
|
1176
1225
|
private clearInterval;
|
|
@@ -1456,6 +1505,22 @@ declare function updateSession(sessionId: string, status: SessionStatus): Promis
|
|
|
1456
1505
|
declare function fetchStatusUrl(sessionId: string): Promise<StatusUrlResponse>;
|
|
1457
1506
|
declare function fetchProviderConfigs(providerId: string, exactProviderVersionString: string | null | undefined, allowedTags: string[] | null | undefined): Promise<ProviderConfigResponse>;
|
|
1458
1507
|
|
|
1508
|
+
/**
|
|
1509
|
+
* Computes the signature required by `initSession` over `{providerId, timestamp}`.
|
|
1510
|
+
*
|
|
1511
|
+
* Use this on a trusted server (where `appSecret` lives) to produce a signature
|
|
1512
|
+
* that can then be passed to `ReclaimProofRequest.initWithSignature(...)` from a
|
|
1513
|
+
* client that never sees the secret.
|
|
1514
|
+
*
|
|
1515
|
+
* @param appSecret - The application secret (private key). Must remain server-side.
|
|
1516
|
+
* @param providerId - The provider id the session will be initialized against.
|
|
1517
|
+
* @param timestamp - The timestamp (ms epoch as string) that will be sent with init.
|
|
1518
|
+
* The same value MUST be passed to `initWithSignature`.
|
|
1519
|
+
*/
|
|
1520
|
+
declare function generateInitSignature(appSecret: string, providerId: string, timestamp: string): Promise<string>;
|
|
1521
|
+
|
|
1522
|
+
declare function generateAttestationNonce(appSecret: string, applicationId: string, sessionId: string, timestamp: string): string;
|
|
1523
|
+
|
|
1459
1524
|
declare function createSignDataForClaim(data: CompleteClaimData): string;
|
|
1460
1525
|
declare function getIdentifierFromClaimInfo(info: ClaimInfo): ClaimID;
|
|
1461
1526
|
/**
|
|
@@ -1509,6 +1574,10 @@ type TeeVerificationResult = {
|
|
|
1509
1574
|
* was generated for your application.
|
|
1510
1575
|
* Returns a result object with `isVerified` and an optional `error` message.
|
|
1511
1576
|
*
|
|
1577
|
+
* This check is stateless and does not protect against replay of a previously
|
|
1578
|
+
* valid proof. Callers must enforce session matching and dedup `sessionId` on
|
|
1579
|
+
* their server. See the README's "Replay Protection" section.
|
|
1580
|
+
*
|
|
1512
1581
|
* @param proof - The proof containing TEE attestation data
|
|
1513
1582
|
* @param appSecret - Your application secret (Ethereum private key). Used to
|
|
1514
1583
|
* derive the application ID and recompute the attestation nonce.
|
|
@@ -1565,4 +1634,4 @@ declare function isDesktopDevice(): boolean;
|
|
|
1565
1634
|
*/
|
|
1566
1635
|
declare function clearDeviceCache(): void;
|
|
1567
1636
|
|
|
1568
|
-
export { type Beacon, type BeaconState, type BodySniff, ClaimCreationType, type ClaimID, type ClaimInfo, type CompleteClaimData, type Context, type CreateVerificationRequest, DeviceType, type EmbeddedFlowHandle, type ExtensionMessage, type FlowHandle, type HashRequirement, type HashableHttpProviderClaimParams, type HttpFormEntry, type HttpProviderClaimParams, type HttpRedirectionMethod, type HttpRedirectionOptions, type InitSessionResponse, type InjectedRequestSpec, type InterceptorRequestSpec, type ModalOptions, type OnError, type OnSuccess, type Proof, type ProofPropertiesJSON, type ProofRequestOptions, type ProviderClaimData, type ProviderConfigResponse, type ProviderHashRequirementSpec, type ProviderHashRequirementsConfig, type ProviderHashRequirementsResponse, type ProviderVersionConfig, type ProviderVersionInfo, RECLAIM_EXTENSION_ACTIONS, type ReclaimFlowInitOptions, type ReclaimFlowLaunchOptions, ReclaimProofRequest, type ReclaimProviderConfig, type ReclaimProviderConfigWithRequestSpec, type RequestSpec, type ResponseMatchSpec, type ResponseRedactionSpec, SUPPORTED_TEE_ATTESTATION_VERSIONS, type SerializableModalOptions, SessionStatus, type SignedClaim, type StartSessionParams, type StatusUrlResponse, type TeeAttestation, type TeeAttestationConfig, type TeeAttestationVersion, TeeVerificationError, type TeeVerificationResult, type TemplateData, type TrustedData, type UpdateSessionResponse, type ValidationConfig, type ValidationConfigWithDisabledValidation, type ValidationConfigWithHash, type ValidationConfigWithProviderInformation, type VerificationConfig, type VerifyProofResult, type VerifyProofResultFailure, type VerifyProofResultSuccess, type WitnessData, assertValidProofsByHash, assertValidateProof, assertVerifiedProof, clearDeviceCache, createLinkWithTemplateData, createSignDataForClaim, fetchProviderConfigs, fetchProviderHashRequirementsBy, fetchStatusUrl, generateSpecsFromRequestSpecTemplate, getAttestors, getDeviceType, getHttpProviderClaimParamsFromProof, getIdentifierFromClaimInfo, getMobileDeviceType, getProviderHashRequirementSpecFromProviderConfig, getProviderHashRequirementsFromSpec, getProviderParamsAsCanonicalizedString, getShortenedUrl, hashProofClaimParams, hashRequestSpec, initSession, isDesktopDevice, isHttpProviderClaimParams, isMobileDevice, recoverSignersOfSignedClaim, runTeeVerification, takePairsWhereValueIsArray, takeTemplateParametersFromProofs, transformForOnchain, updateSession, verifyProof, verifyTeeAttestation };
|
|
1637
|
+
export { type Beacon, type BeaconState, type BodySniff, ClaimCreationType, type ClaimID, type ClaimInfo, type CompleteClaimData, type Context, type CreateVerificationRequest, DeviceType, type EmbeddedFlowHandle, type ExtensionMessage, type FlowHandle, type HashRequirement, type HashableHttpProviderClaimParams, type HttpFormEntry, type HttpProviderClaimParams, type HttpRedirectionMethod, type HttpRedirectionOptions, type InitSessionResponse, type InjectedRequestSpec, type InterceptorRequestSpec, type ModalOptions, type OnError, type OnSuccess, type Proof, type ProofPropertiesJSON, type ProofRequestOptions, type ProviderClaimData, type ProviderConfigResponse, type ProviderHashRequirementSpec, type ProviderHashRequirementsConfig, type ProviderHashRequirementsResponse, type ProviderVersionConfig, type ProviderVersionInfo, RECLAIM_EXTENSION_ACTIONS, type ReclaimFlowInitOptions, type ReclaimFlowLaunchOptions, ReclaimProofRequest, type ReclaimProviderConfig, type ReclaimProviderConfigWithRequestSpec, type RequestSpec, type ResponseMatchSpec, type ResponseRedactionSpec, SUPPORTED_TEE_ATTESTATION_VERSIONS, type SerializableModalOptions, SessionStatus, type SignedClaim, type StartSessionParams, type StatusUrlResponse, type TeeAttestation, type TeeAttestationConfig, type TeeAttestationVersion, TeeVerificationError, type TeeVerificationResult, type TemplateData, type TrustedData, type UpdateSessionResponse, type ValidationConfig, type ValidationConfigWithDisabledValidation, type ValidationConfigWithHash, type ValidationConfigWithProviderInformation, type VerificationConfig, type VerifyProofResult, type VerifyProofResultFailure, type VerifyProofResultSuccess, type WitnessData, assertValidProofsByHash, assertValidateProof, assertVerifiedProof, clearDeviceCache, createLinkWithTemplateData, createSignDataForClaim, fetchProviderConfigs, fetchProviderHashRequirementsBy, fetchStatusUrl, generateAttestationNonce, generateInitSignature, generateSpecsFromRequestSpecTemplate, getAttestors, getDeviceType, getHttpProviderClaimParamsFromProof, getIdentifierFromClaimInfo, getMobileDeviceType, getProviderHashRequirementSpecFromProviderConfig, getProviderHashRequirementsFromSpec, getProviderParamsAsCanonicalizedString, getShortenedUrl, hashProofClaimParams, hashRequestSpec, initSession, isDesktopDevice, isHttpProviderClaimParams, isMobileDevice, recoverSignersOfSignedClaim, runTeeVerification, takePairsWhereValueIsArray, takeTemplateParametersFromProofs, transformForOnchain, updateSession, verifyProof, verifyTeeAttestation };
|
package/dist/index.js
CHANGED
|
@@ -84,7 +84,7 @@ var require_package = __commonJS({
|
|
|
84
84
|
"package.json"(exports2, module2) {
|
|
85
85
|
module2.exports = {
|
|
86
86
|
name: "@reclaimprotocol/js-sdk",
|
|
87
|
-
version: "5.
|
|
87
|
+
version: "5.4.0",
|
|
88
88
|
description: "Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.",
|
|
89
89
|
main: "dist/index.js",
|
|
90
90
|
types: "dist/index.d.ts",
|
|
@@ -203,6 +203,8 @@ __export(index_exports, {
|
|
|
203
203
|
fetchProviderConfigs: () => fetchProviderConfigs,
|
|
204
204
|
fetchProviderHashRequirementsBy: () => fetchProviderHashRequirementsBy,
|
|
205
205
|
fetchStatusUrl: () => fetchStatusUrl,
|
|
206
|
+
generateAttestationNonce: () => generateAttestationNonce,
|
|
207
|
+
generateInitSignature: () => generateInitSignature,
|
|
206
208
|
generateSpecsFromRequestSpecTemplate: () => generateSpecsFromRequestSpecTemplate,
|
|
207
209
|
getAttestors: () => getAttestors,
|
|
208
210
|
getDeviceType: () => getDeviceType,
|
|
@@ -2255,81 +2257,7 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
2255
2257
|
{ paramName: "providerId", input: providerId, isString: true },
|
|
2256
2258
|
{ paramName: "appSecret", input: appSecret, isString: true }
|
|
2257
2259
|
], "the constructor");
|
|
2258
|
-
|
|
2259
|
-
if (options.acceptAiProviders) {
|
|
2260
|
-
validateFunctionParams([
|
|
2261
|
-
{ paramName: "acceptAiProviders", input: options.acceptAiProviders }
|
|
2262
|
-
], "the constructor");
|
|
2263
|
-
}
|
|
2264
|
-
if (options.providerVersion) {
|
|
2265
|
-
validateFunctionParams([
|
|
2266
|
-
{ paramName: "providerVersion", input: options.providerVersion, isString: true }
|
|
2267
|
-
], "the constructor");
|
|
2268
|
-
}
|
|
2269
|
-
if (options.log) {
|
|
2270
|
-
validateFunctionParams([
|
|
2271
|
-
{ paramName: "log", input: options.log }
|
|
2272
|
-
], "the constructor");
|
|
2273
|
-
}
|
|
2274
|
-
if (options.useAppClip) {
|
|
2275
|
-
validateFunctionParams([
|
|
2276
|
-
{ paramName: "useAppClip", input: options.useAppClip }
|
|
2277
|
-
], "the constructor");
|
|
2278
|
-
}
|
|
2279
|
-
if (options.device) {
|
|
2280
|
-
validateFunctionParams([
|
|
2281
|
-
{ paramName: "device", input: options.device, isString: true }
|
|
2282
|
-
], "the constructor");
|
|
2283
|
-
}
|
|
2284
|
-
if (options.useBrowserExtension) {
|
|
2285
|
-
validateFunctionParams([
|
|
2286
|
-
{ paramName: "useBrowserExtension", input: options.useBrowserExtension }
|
|
2287
|
-
], "the constructor");
|
|
2288
|
-
}
|
|
2289
|
-
if (options.extensionID) {
|
|
2290
|
-
validateFunctionParams([
|
|
2291
|
-
{ paramName: "extensionID", input: options.extensionID, isString: true }
|
|
2292
|
-
], "the constructor");
|
|
2293
|
-
}
|
|
2294
|
-
if (options.envUrl) {
|
|
2295
|
-
validateFunctionParams([
|
|
2296
|
-
{ paramName: "envUrl", input: options.envUrl, isString: true }
|
|
2297
|
-
], "the constructor");
|
|
2298
|
-
}
|
|
2299
|
-
if (options.portalUrl) {
|
|
2300
|
-
validateFunctionParams([
|
|
2301
|
-
{ paramName: "portalUrl", input: options.portalUrl, isString: true }
|
|
2302
|
-
], "the constructor");
|
|
2303
|
-
}
|
|
2304
|
-
if (options.customSharePageUrl) {
|
|
2305
|
-
validateFunctionParams([
|
|
2306
|
-
{ paramName: "customSharePageUrl", input: options.customSharePageUrl, isString: true }
|
|
2307
|
-
], "the constructor");
|
|
2308
|
-
}
|
|
2309
|
-
if (options.customAppClipUrl) {
|
|
2310
|
-
validateFunctionParams([
|
|
2311
|
-
{ paramName: "customAppClipUrl", input: options.customAppClipUrl, isString: true }
|
|
2312
|
-
], "the constructor");
|
|
2313
|
-
}
|
|
2314
|
-
if (options.preferredLocale) {
|
|
2315
|
-
validateFunctionParams([
|
|
2316
|
-
{ paramName: "preferredLocale", input: options.preferredLocale, isString: true }
|
|
2317
|
-
], "the constructor");
|
|
2318
|
-
validateFunctionParamsWithFn({
|
|
2319
|
-
paramName: "preferredLocale",
|
|
2320
|
-
input: options.preferredLocale,
|
|
2321
|
-
isValid: () => {
|
|
2322
|
-
try {
|
|
2323
|
-
Intl.getCanonicalLocales(options.preferredLocale);
|
|
2324
|
-
return true;
|
|
2325
|
-
} catch (error) {
|
|
2326
|
-
logger10.info("Failed to canonicalize locale", error);
|
|
2327
|
-
return false;
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
}, "the constructor");
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2260
|
+
_ReclaimProofRequest.validateInitOptions(options, "the constructor");
|
|
2333
2261
|
const proofRequestOptions = __spreadProps(__spreadValues({}, options), {
|
|
2334
2262
|
acceptTeeAttestation: (_a = options == null ? void 0 : options.acceptTeeAttestation) != null ? _a : true
|
|
2335
2263
|
});
|
|
@@ -2362,6 +2290,95 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
2362
2290
|
}
|
|
2363
2291
|
});
|
|
2364
2292
|
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Initializes a new Reclaim proof request using a signature computed externally
|
|
2295
|
+
* (e.g. on a trusted backend), so `appSecret` never has to live on the client.
|
|
2296
|
+
*
|
|
2297
|
+
* The signature must be produced over `canonicalize({ providerId, timestamp })`
|
|
2298
|
+
* using the application's `appSecret` — see `generateInitSignature()` for the
|
|
2299
|
+
* exact algorithm. The same `timestamp` used at signing time must be passed here.
|
|
2300
|
+
*
|
|
2301
|
+
* TEE attestation: the attestation nonce depends on `sessionId`, which is only
|
|
2302
|
+
* known after the backend init call. To use TEE without exposing `appSecret`,
|
|
2303
|
+
* pass an async `getAttestationNonce` callback that derives the nonce on your
|
|
2304
|
+
* server using `generateAttestationNonce(appSecret, applicationId, sessionId, timestamp)`.
|
|
2305
|
+
* If `acceptTeeAttestation` is left enabled but no callback is provided, init throws.
|
|
2306
|
+
*
|
|
2307
|
+
* @param applicationId - Your Reclaim application ID
|
|
2308
|
+
* @param providerId - The ID of the provider to use for proof generation
|
|
2309
|
+
* @param sessionAuth - Pre-computed signature, the timestamp it was signed over,
|
|
2310
|
+
* and an optional async callback to compute the attestation nonce.
|
|
2311
|
+
* @param options - Optional configuration options for the proof request
|
|
2312
|
+
*
|
|
2313
|
+
* @example
|
|
2314
|
+
* ```typescript
|
|
2315
|
+
* // Backend (Node):
|
|
2316
|
+
* const timestamp = Date.now().toString();
|
|
2317
|
+
* const signature = await generateInitSignature(APP_SECRET, providerId, timestamp);
|
|
2318
|
+
* // ...return { signature, timestamp } to the client...
|
|
2319
|
+
*
|
|
2320
|
+
* // Client:
|
|
2321
|
+
* const proofRequest = await ReclaimProofRequest.initWithSignature(
|
|
2322
|
+
* applicationId,
|
|
2323
|
+
* providerId,
|
|
2324
|
+
* { signature, timestamp },
|
|
2325
|
+
* { acceptTeeAttestation: false }
|
|
2326
|
+
* );
|
|
2327
|
+
* ```
|
|
2328
|
+
*/
|
|
2329
|
+
static initWithSignature(applicationId, providerId, sessionAuth, options) {
|
|
2330
|
+
return __async(this, null, function* () {
|
|
2331
|
+
var _a;
|
|
2332
|
+
try {
|
|
2333
|
+
validateFunctionParams([
|
|
2334
|
+
{ paramName: "applicationId", input: applicationId, isString: true },
|
|
2335
|
+
{ paramName: "providerId", input: providerId, isString: true },
|
|
2336
|
+
{ paramName: "signature", input: sessionAuth == null ? void 0 : sessionAuth.signature, isString: true },
|
|
2337
|
+
{ paramName: "timestamp", input: sessionAuth == null ? void 0 : sessionAuth.timestamp, isString: true }
|
|
2338
|
+
], "initWithSignature");
|
|
2339
|
+
_ReclaimProofRequest.validateInitOptions(options, "initWithSignature");
|
|
2340
|
+
const proofRequestOptions = __spreadProps(__spreadValues({}, options), {
|
|
2341
|
+
acceptTeeAttestation: (_a = options == null ? void 0 : options.acceptTeeAttestation) != null ? _a : true
|
|
2342
|
+
});
|
|
2343
|
+
if (proofRequestOptions.acceptTeeAttestation && !sessionAuth.getAttestationNonce) {
|
|
2344
|
+
throw new InvalidParamError(
|
|
2345
|
+
"initWithSignature requires a `getAttestationNonce` callback when `acceptTeeAttestation` is enabled. Either pass `acceptTeeAttestation: false`, or provide a callback that computes the nonce server-side using `generateAttestationNonce(appSecret, applicationId, sessionId, timestamp)`."
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
const proofRequestInstance = new _ReclaimProofRequest(applicationId, providerId, proofRequestOptions);
|
|
2349
|
+
proofRequestInstance.timeStamp = sessionAuth.timestamp;
|
|
2350
|
+
proofRequestInstance.setSignature(sessionAuth.signature);
|
|
2351
|
+
const data = yield initSession(
|
|
2352
|
+
providerId,
|
|
2353
|
+
applicationId,
|
|
2354
|
+
sessionAuth.timestamp,
|
|
2355
|
+
sessionAuth.signature,
|
|
2356
|
+
options == null ? void 0 : options.providerVersion
|
|
2357
|
+
);
|
|
2358
|
+
proofRequestInstance.sessionId = data.sessionId;
|
|
2359
|
+
proofRequestInstance.resolvedProviderVersion = data.resolvedProviderVersion;
|
|
2360
|
+
proofRequestInstance.context.reclaimSessionId = data.sessionId;
|
|
2361
|
+
if (proofRequestOptions.acceptTeeAttestation && sessionAuth.getAttestationNonce) {
|
|
2362
|
+
const attestationNonce = yield sessionAuth.getAttestationNonce(data.sessionId);
|
|
2363
|
+
validateFunctionParams(
|
|
2364
|
+
[{ input: attestationNonce, paramName: "attestationNonce", isString: true }],
|
|
2365
|
+
"initWithSignature"
|
|
2366
|
+
);
|
|
2367
|
+
proofRequestInstance.setAttestationContext(attestationNonce, {
|
|
2368
|
+
applicationId,
|
|
2369
|
+
sessionId: data.sessionId,
|
|
2370
|
+
timestamp: sessionAuth.timestamp,
|
|
2371
|
+
attestationVersion: SDK_TEE_ATTESTATION_VERSION
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
return proofRequestInstance;
|
|
2375
|
+
} catch (error) {
|
|
2376
|
+
console.error(error);
|
|
2377
|
+
logger10.info("Failed to initialize ReclaimProofRequest with signature", error);
|
|
2378
|
+
throw new InitError("Failed to initialize ReclaimProofRequest with signature", error);
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2365
2382
|
/**
|
|
2366
2383
|
* Creates a ReclaimProofRequest instance from a JSON string representation
|
|
2367
2384
|
*
|
|
@@ -2855,6 +2872,58 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
2855
2872
|
}
|
|
2856
2873
|
return this.sessionId;
|
|
2857
2874
|
}
|
|
2875
|
+
static validateInitOptions(options, caller) {
|
|
2876
|
+
if (!options) return;
|
|
2877
|
+
if (options.acceptAiProviders) {
|
|
2878
|
+
validateFunctionParams([{ paramName: "acceptAiProviders", input: options.acceptAiProviders }], caller);
|
|
2879
|
+
}
|
|
2880
|
+
if (options.providerVersion) {
|
|
2881
|
+
validateFunctionParams([{ paramName: "providerVersion", input: options.providerVersion, isString: true }], caller);
|
|
2882
|
+
}
|
|
2883
|
+
if (options.log) {
|
|
2884
|
+
validateFunctionParams([{ paramName: "log", input: options.log }], caller);
|
|
2885
|
+
}
|
|
2886
|
+
if (options.useAppClip) {
|
|
2887
|
+
validateFunctionParams([{ paramName: "useAppClip", input: options.useAppClip }], caller);
|
|
2888
|
+
}
|
|
2889
|
+
if (options.device) {
|
|
2890
|
+
validateFunctionParams([{ paramName: "device", input: options.device, isString: true }], caller);
|
|
2891
|
+
}
|
|
2892
|
+
if (options.useBrowserExtension) {
|
|
2893
|
+
validateFunctionParams([{ paramName: "useBrowserExtension", input: options.useBrowserExtension }], caller);
|
|
2894
|
+
}
|
|
2895
|
+
if (options.extensionID) {
|
|
2896
|
+
validateFunctionParams([{ paramName: "extensionID", input: options.extensionID, isString: true }], caller);
|
|
2897
|
+
}
|
|
2898
|
+
if (options.envUrl) {
|
|
2899
|
+
validateFunctionParams([{ paramName: "envUrl", input: options.envUrl, isString: true }], caller);
|
|
2900
|
+
}
|
|
2901
|
+
if (options.portalUrl) {
|
|
2902
|
+
validateFunctionParams([{ paramName: "portalUrl", input: options.portalUrl, isString: true }], caller);
|
|
2903
|
+
}
|
|
2904
|
+
if (options.customSharePageUrl) {
|
|
2905
|
+
validateFunctionParams([{ paramName: "customSharePageUrl", input: options.customSharePageUrl, isString: true }], caller);
|
|
2906
|
+
}
|
|
2907
|
+
if (options.customAppClipUrl) {
|
|
2908
|
+
validateFunctionParams([{ paramName: "customAppClipUrl", input: options.customAppClipUrl, isString: true }], caller);
|
|
2909
|
+
}
|
|
2910
|
+
if (options.preferredLocale) {
|
|
2911
|
+
validateFunctionParams([{ paramName: "preferredLocale", input: options.preferredLocale, isString: true }], caller);
|
|
2912
|
+
validateFunctionParamsWithFn({
|
|
2913
|
+
paramName: "preferredLocale",
|
|
2914
|
+
input: options.preferredLocale,
|
|
2915
|
+
isValid: () => {
|
|
2916
|
+
try {
|
|
2917
|
+
Intl.getCanonicalLocales(options.preferredLocale);
|
|
2918
|
+
return true;
|
|
2919
|
+
} catch (error) {
|
|
2920
|
+
logger10.info("Failed to canonicalize locale", error);
|
|
2921
|
+
return false;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
}, caller);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2858
2927
|
// Private helper methods
|
|
2859
2928
|
setSignature(signature) {
|
|
2860
2929
|
try {
|
|
@@ -3533,6 +3602,33 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3533
3602
|
return this.jsonProofResponse;
|
|
3534
3603
|
}
|
|
3535
3604
|
};
|
|
3605
|
+
|
|
3606
|
+
// src/utils/signatureUtils.ts
|
|
3607
|
+
var import_ethers7 = require("ethers");
|
|
3608
|
+
var import_canonicalize4 = __toESM(require("canonicalize"));
|
|
3609
|
+
function generateInitSignature(appSecret, providerId, timestamp) {
|
|
3610
|
+
return __async(this, null, function* () {
|
|
3611
|
+
validateFunctionParams([
|
|
3612
|
+
{ input: appSecret, paramName: "appSecret", isString: true },
|
|
3613
|
+
{ input: providerId, paramName: "providerId", isString: true },
|
|
3614
|
+
{ input: timestamp, paramName: "timestamp", isString: true }
|
|
3615
|
+
], "generateInitSignature");
|
|
3616
|
+
try {
|
|
3617
|
+
const wallet = new import_ethers7.ethers.Wallet(appSecret);
|
|
3618
|
+
const canonicalData = (0, import_canonicalize4.default)({ providerId, timestamp });
|
|
3619
|
+
if (!canonicalData) {
|
|
3620
|
+
throw new SignatureGeneratingError("Failed to canonicalize data for signing.");
|
|
3621
|
+
}
|
|
3622
|
+
const messageHash = import_ethers7.ethers.keccak256(new TextEncoder().encode(canonicalData));
|
|
3623
|
+
return yield wallet.signMessage(import_ethers7.ethers.getBytes(messageHash));
|
|
3624
|
+
} catch (err) {
|
|
3625
|
+
throw new SignatureGeneratingError(
|
|
3626
|
+
`Error generating init signature for providerId: ${providerId}`,
|
|
3627
|
+
err
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3536
3632
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3537
3633
|
0 && (module.exports = {
|
|
3538
3634
|
ReclaimProofRequest,
|
|
@@ -3546,6 +3642,8 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3546
3642
|
fetchProviderConfigs,
|
|
3547
3643
|
fetchProviderHashRequirementsBy,
|
|
3548
3644
|
fetchStatusUrl,
|
|
3645
|
+
generateAttestationNonce,
|
|
3646
|
+
generateInitSignature,
|
|
3549
3647
|
generateSpecsFromRequestSpecTemplate,
|
|
3550
3648
|
getAttestors,
|
|
3551
3649
|
getDeviceType,
|