@reclaimprotocol/client 0.1.29 → 0.1.31

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.
Files changed (110) hide show
  1. package/README.md +255 -234
  2. package/lib/api.d.ts +2 -0
  3. package/lib/api.js +2 -0
  4. package/lib/client.js +2 -0
  5. package/lib/enckeys.d.ts +1 -1
  6. package/lib/enckeys.js +2 -2
  7. package/lib/encoding.d.ts +4 -0
  8. package/lib/encoding.js +10 -0
  9. package/lib/feature-flags.d.ts +9 -0
  10. package/lib/feature-flags.js +19 -0
  11. package/lib/index.d.ts +4 -4
  12. package/lib/index.js +4 -4
  13. package/lib/jwks.d.ts +8 -8
  14. package/lib/jwks.js +38 -17
  15. package/lib/launch/destination.d.ts +14 -0
  16. package/lib/launch/destination.js +89 -0
  17. package/lib/launch/device.d.ts +30 -0
  18. package/lib/launch/device.js +168 -0
  19. package/lib/launch/environment.d.ts +224 -0
  20. package/lib/launch/environment.js +98 -0
  21. package/lib/launch/errors.d.ts +20 -0
  22. package/lib/launch/errors.js +25 -0
  23. package/lib/launch/extension.d.ts +42 -0
  24. package/lib/launch/extension.js +72 -0
  25. package/lib/launch/flow.d.ts +82 -0
  26. package/lib/launch/flow.js +222 -0
  27. package/lib/launch/index.d.ts +16 -0
  28. package/lib/launch/index.js +12 -0
  29. package/lib/launch/mobile.d.ts +77 -0
  30. package/lib/launch/mobile.js +124 -0
  31. package/lib/launch/modal.d.ts +50 -0
  32. package/lib/launch/modal.js +258 -0
  33. package/lib/launch/options.d.ts +74 -0
  34. package/lib/launch/options.js +20 -0
  35. package/lib/launch/portal.d.ts +28 -0
  36. package/lib/launch/portal.js +68 -0
  37. package/lib/launch/qr.d.ts +5 -0
  38. package/lib/launch/qr.js +28 -0
  39. package/lib/launch/target.d.ts +46 -0
  40. package/lib/launch/target.js +128 -0
  41. package/lib/launch/vendor/qrcodegen.d.ts +134 -0
  42. package/lib/launch/vendor/qrcodegen.js +822 -0
  43. package/lib/legacy-proof.d.ts +2 -0
  44. package/lib/legacy-proof.js +37 -7
  45. package/lib/proof-hash.d.ts +5 -5
  46. package/lib/proof-hash.js +1 -1
  47. package/lib/proof-recipe.d.ts +3 -3
  48. package/lib/proof-recipe.js +1 -1
  49. package/lib/proof-types.d.ts +35 -8
  50. package/lib/reclaim.d.ts +81 -70
  51. package/lib/reclaim.js +79 -130
  52. package/lib/request-proof.d.ts +60 -0
  53. package/lib/request-proof.js +241 -0
  54. package/lib/result.d.ts +46 -38
  55. package/lib/result.js +160 -100
  56. package/lib/routes.gen.d.ts +76 -28
  57. package/lib/routes.gen.js +24 -11
  58. package/lib/tee-binding.d.ts +1 -1
  59. package/lib/types/openapi.d.ts +1 -0
  60. package/lib/types/openapi.gen.d.ts +1594 -488
  61. package/lib/types/openapi.gen.js +5 -0
  62. package/lib/verification-clients.d.ts +28 -14
  63. package/lib/verification-clients.js +31 -11
  64. package/lib/verify-attestor-tee.d.ts +19 -2
  65. package/lib/verify-attestor-tee.js +40 -4
  66. package/lib/verify-tee.d.ts +10 -1
  67. package/lib/verify-tee.js +34 -10
  68. package/package.json +5 -1
  69. package/src/api.ts +9 -0
  70. package/src/client.ts +2 -0
  71. package/src/enckeys.ts +2 -2
  72. package/src/encoding.ts +12 -0
  73. package/src/feature-flags.ts +39 -0
  74. package/src/index.ts +13 -14
  75. package/src/jwks.ts +52 -30
  76. package/src/launch/destination.ts +111 -0
  77. package/src/launch/device.ts +242 -0
  78. package/src/launch/environment.ts +337 -0
  79. package/src/launch/errors.ts +49 -0
  80. package/src/launch/extension.ts +124 -0
  81. package/src/launch/flow.ts +369 -0
  82. package/src/launch/index.ts +42 -0
  83. package/src/launch/mobile.ts +184 -0
  84. package/src/launch/modal.ts +378 -0
  85. package/src/launch/options.ts +99 -0
  86. package/src/launch/portal.ts +104 -0
  87. package/src/launch/qr.ts +37 -0
  88. package/src/launch/target.ts +150 -0
  89. package/src/launch/vendor/qrcodegen.ts +1034 -0
  90. package/src/proof-hash.ts +6 -6
  91. package/src/proof-recipe.ts +3 -3
  92. package/src/proof-types.ts +36 -8
  93. package/src/reclaim.ts +185 -218
  94. package/src/{legacy-proof.ts → request-proof.ts} +81 -36
  95. package/src/result.ts +238 -172
  96. package/src/routes.gen.ts +24 -11
  97. package/src/tee-binding.ts +1 -1
  98. package/src/types/openapi.gen.ts +1599 -488
  99. package/src/types/openapi.ts +3 -1
  100. package/src/verification-clients.ts +33 -19
  101. package/src/verify-attestor-tee.ts +66 -8
  102. package/src/verify-tee.ts +44 -13
  103. package/lib/api-client.d.ts +0 -31
  104. package/lib/api-client.js +0 -112
  105. package/lib/api-consts.d.ts +0 -4
  106. package/lib/api-consts.js +0 -4
  107. package/lib/api-errors.d.ts +0 -13
  108. package/lib/api-errors.js +0 -30
  109. package/lib/eip712.d.ts +0 -20
  110. package/lib/eip712.js +0 -108
@@ -4,7 +4,7 @@ import { fetchReclaimJwks, trustedAttestorAddresses } from './jwks.ts'
4
4
  import { assertValidProofHashes, getHashFromProof } from './proof-hash.ts'
5
5
  import type {
6
6
  HashRequirement,
7
- LegacyProof,
7
+ ReclaimRequestProof,
8
8
  TrustedData,
9
9
  } from './proof-types.ts'
10
10
  import {
@@ -13,9 +13,9 @@ import {
13
13
  } from './verify-attestor-tee.ts'
14
14
  import { runTeeVerification, type TeeAttestationConfig } from './verify-tee.ts'
15
15
 
16
- export type { LegacyProof } from './proof-types.ts'
16
+ export type { ReclaimRequestProof } from './proof-types.ts'
17
17
 
18
- /** A proof hash requirement accepted by legacy `verifyProof`. */
18
+ /** A proof hash requirement accepted by `verifyRequestProof`. */
19
19
  export type ProofHashRequirement = string | HashRequirement
20
20
 
21
21
  export interface ResolvedSessionProofValidation {
@@ -30,8 +30,9 @@ export interface ResolvedProviderProofValidation {
30
30
  hashes: ProofHashRequirement[]
31
31
  }
32
32
 
33
- /** Options intentionally preserve legacy `verifyProof` vocabulary. */
34
- export interface LegacyVerifyProofOptions {
33
+ /** Field names match the request spec a provider recipe declares, so a
34
+ * caller configuring verification reads the same vocabulary it authored. */
35
+ export interface VerifyRequestProofOptions {
35
36
  hashes?: ProofHashRequirement[]
36
37
  providerId?: string
37
38
  providerVersion?: string
@@ -50,7 +51,7 @@ export interface LegacyVerifyProofOptions {
50
51
  sessionId?: string
51
52
  resolveSession?: (
52
53
  sessionId: string,
53
- proofs: LegacyProof[],
54
+ proofs: ReclaimRequestProof[],
54
55
  ) => Promise<ResolvedSessionProofValidation>
55
56
  | ResolvedSessionProofValidation
56
57
  /** Resolve an exact provider without coupling this verifier to HTTP. */
@@ -58,7 +59,7 @@ export interface LegacyVerifyProofOptions {
58
59
  providerId: string,
59
60
  providerVersion: string | undefined,
60
61
  allowedTags: string[] | undefined,
61
- proofs: LegacyProof[],
62
+ proofs: ReclaimRequestProof[],
62
63
  ) => Promise<ResolvedProviderProofValidation[]>
63
64
  | ResolvedProviderProofValidation[]
64
65
  expectedSessionId?: string
@@ -66,29 +67,31 @@ export interface LegacyVerifyProofOptions {
66
67
  expectedContext?: Record<string, unknown>
67
68
  }
68
69
 
69
- export interface LegacyProofOutcome {
70
+ export interface ReclaimRequestProofOutcome {
70
71
  valid: boolean
71
72
  reason?: string
72
73
  proofHashes?: string[]
73
74
  isTeeAttestationVerified?: boolean
75
+ /** True when at least one witness's `attestation_report` verified; false
76
+ * when the check ran and no witness carried one. */
74
77
  isAttestorTeeAttestationVerified?: boolean
75
78
  data?: TrustedData[]
76
79
  publicData?: unknown[]
77
80
  }
78
81
 
79
- /** Return the provider hashes encoded in an exact legacy proof. */
80
- export function proofHashes(proof: LegacyProof): string[] {
82
+ /** Return the provider hashes encoded in an exact request proof. */
83
+ export function proofHashes(proof: ReclaimRequestProof): string[] {
81
84
  return getHashFromProof(proof)
82
85
  }
83
86
 
84
87
  /**
85
- * Verify exact legacy proofs without depending on `@reclaimprotocol/js-sdk`.
88
+ * Verify exact request proofs without depending on `@reclaimprotocol/js-sdk`.
86
89
  * The signed claim is never renamed or reconstructed.
87
90
  */
88
- export async function verifyProof(
89
- proofOrProofs: LegacyProof | LegacyProof[],
90
- config: LegacyVerifyProofOptions,
91
- ): Promise<LegacyProofOutcome> {
91
+ export async function verifyRequestProof(
92
+ proofOrProofs: ReclaimRequestProof | ReclaimRequestProof[],
93
+ config: VerifyRequestProofOptions,
94
+ ): Promise<ReclaimRequestProofOutcome> {
92
95
  const proofs = Array.isArray(proofOrProofs) ? proofOrProofs : [proofOrProofs]
93
96
  if(!proofs.length) {
94
97
  return { valid: false, reason: 'no-proofs' }
@@ -97,8 +100,8 @@ export async function verifyProof(
97
100
  try {
98
101
  const effective = await resolveValidation(config, proofs)
99
102
  assertSessionAndContext(proofs, effective)
100
- const attestors = await resolveTrustedAttestors(effective)
101
- assertVerifiedSignatures(proofs, attestors)
103
+ const trust = await resolveAttestorTrust(effective)
104
+ assertVerifiedSignatures(proofs, trust.addresses)
102
105
  await assertContent(proofs, effective)
103
106
 
104
107
  let isTeeAttestationVerified: boolean | undefined
@@ -108,12 +111,20 @@ export async function verifyProof(
108
111
  }
109
112
 
110
113
  let isAttestorTeeAttestationVerified: boolean | undefined
111
- if(effective.attestorTeeAttestation) {
112
- await runAttestorTeeVerification(
114
+ const attestorTeeAttestation = resolveAttestorTeeAttestation(
115
+ effective.attestorTeeAttestation,
116
+ trust.imageDigests,
117
+ )
118
+ if(attestorTeeAttestation) {
119
+ // `verified` counts witnesses whose attestation report was checked.
120
+ // A proof whose witnesses carry no report still passes the address
121
+ // and signature bindings, but nothing was attested — say so rather
122
+ // than reporting a TEE attestation that never happened.
123
+ const summary = await runAttestorTeeVerification(
113
124
  proofs,
114
- effective.attestorTeeAttestation,
125
+ attestorTeeAttestation,
115
126
  )
116
- isAttestorTeeAttestationVerified = true
127
+ isAttestorTeeAttestationVerified = summary.verified > 0
117
128
  }
118
129
 
119
130
  return {
@@ -133,8 +144,8 @@ export async function verifyProof(
133
144
  }
134
145
 
135
146
  async function resolveValidation(
136
- config: LegacyVerifyProofOptions,
137
- proofs: LegacyProof[],
147
+ config: VerifyRequestProofOptions,
148
+ proofs: ReclaimRequestProof[],
138
149
  ) {
139
150
  let effective = config
140
151
  if(config.sessionId && !hasContentValidation(config)) {
@@ -151,8 +162,9 @@ async function resolveValidation(
151
162
  return effective
152
163
  }
153
164
 
154
- async function resolveTrustedAttestors(config: LegacyVerifyProofOptions) {
165
+ async function resolveAttestorTrust(config: VerifyRequestProofOptions) {
155
166
  const addresses = config.attestorAddresses?.map(normalized) ?? []
167
+ const imageDigests: string[] = []
156
168
  if(config.attestorJwksUrl) {
157
169
  const registry = await fetchReclaimJwks(
158
170
  config.attestorJwksUrl,
@@ -162,6 +174,9 @@ async function resolveTrustedAttestors(config: LegacyVerifyProofOptions) {
162
174
  ...(registry['x-reclaim-attestors'] ?? []),
163
175
  ...trustedAttestorAddresses(registry.keys),
164
176
  )
177
+ imageDigests.push(
178
+ ...(registry['x-reclaim-attestor-image-digests'] ?? []),
179
+ )
165
180
  }
166
181
 
167
182
  const unique = [...new Set(addresses)]
@@ -169,11 +184,41 @@ async function resolveTrustedAttestors(config: LegacyVerifyProofOptions) {
169
184
  throw new Error('attestor-trust-required')
170
185
  }
171
186
 
172
- return unique
187
+ return {
188
+ addresses: unique,
189
+ imageDigests: [...new Set(imageDigests)],
190
+ }
191
+ }
192
+
193
+ function resolveAttestorTeeAttestation(
194
+ configured: AttestorTeeAttestationConfig | undefined,
195
+ registryImageDigests: string[],
196
+ ): AttestorTeeAttestationConfig | undefined {
197
+ if(!registryImageDigests.length) {
198
+ return configured
199
+ }
200
+
201
+ const configuredDigests = configured?.expectedImageDigests
202
+ if(configuredDigests?.length) {
203
+ const registry = new Set(registryImageDigests)
204
+ const expectedImageDigests = [...new Set(configuredDigests
205
+ .map((digest) => digest.trim().toLowerCase())
206
+ .filter((digest) => registry.has(digest)))]
207
+ if(!expectedImageDigests.length) {
208
+ throw new Error('attestor-image-trust-mismatch')
209
+ }
210
+
211
+ return { ...configured, expectedImageDigests }
212
+ }
213
+
214
+ return {
215
+ ...configured,
216
+ expectedImageDigests: registryImageDigests,
217
+ }
173
218
  }
174
219
 
175
220
  function assertVerifiedSignatures(
176
- proofs: LegacyProof[],
221
+ proofs: ReclaimRequestProof[],
177
222
  trustedAttestors: string[],
178
223
  ) {
179
224
  for(const proof of proofs) {
@@ -201,8 +246,8 @@ function assertVerifiedSignatures(
201
246
  }
202
247
 
203
248
  async function assertContent(
204
- proofs: LegacyProof[],
205
- config: LegacyVerifyProofOptions,
249
+ proofs: ReclaimRequestProof[],
250
+ config: VerifyRequestProofOptions,
206
251
  ) {
207
252
  if(config.dangerouslyDisableContentValidation) {
208
253
  return
@@ -245,8 +290,8 @@ async function assertContent(
245
290
  }
246
291
 
247
292
  function assertSessionAndContext(
248
- proofs: LegacyProof[],
249
- config: LegacyVerifyProofOptions,
293
+ proofs: ReclaimRequestProof[],
294
+ config: VerifyRequestProofOptions,
250
295
  ) {
251
296
  const expectedSessionId = config.sessionId ?? config.expectedSessionId
252
297
  for(const proof of proofs) {
@@ -267,7 +312,7 @@ function assertSessionAndContext(
267
312
  }
268
313
  }
269
314
 
270
- function trustedData(proof: LegacyProof): TrustedData {
315
+ function trustedData(proof: ReclaimRequestProof): TrustedData {
271
316
  const context = contextFrom(proof)
272
317
  const extractedParameters = isRecord(context.extractedParameters)
273
318
  ? Object.fromEntries(Object.entries(context.extractedParameters).map(
@@ -279,7 +324,7 @@ function trustedData(proof: LegacyProof): TrustedData {
279
324
  }
280
325
 
281
326
  function collectProofHashes(
282
- proofs: LegacyProof[],
327
+ proofs: ReclaimRequestProof[],
283
328
  hasNoPii: true | undefined,
284
329
  ): string[] | undefined {
285
330
  try {
@@ -289,7 +334,7 @@ function collectProofHashes(
289
334
  }
290
335
  }
291
336
 
292
- function hasContentValidation(config: LegacyVerifyProofOptions): boolean {
337
+ function hasContentValidation(config: VerifyRequestProofOptions): boolean {
293
338
  return Boolean(
294
339
  config.hashes
295
340
  || config.providerId
@@ -315,7 +360,7 @@ function normalized(value: string): string {
315
360
  return value.trim().toLowerCase()
316
361
  }
317
362
 
318
- function contextFrom(proof: LegacyProof): Record<string, unknown> {
363
+ function contextFrom(proof: ReclaimRequestProof): Record<string, unknown> {
319
364
  const raw = proof.claimData.context
320
365
  if(!raw) {
321
366
  return {}
@@ -329,7 +374,7 @@ function contextFrom(proof: LegacyProof): Record<string, unknown> {
329
374
  }
330
375
  }
331
376
 
332
- function sessionIds(proof: LegacyProof): string[] {
377
+ function sessionIds(proof: ReclaimRequestProof): string[] {
333
378
  const context = contextFrom(proof)
334
379
  const ids = [context.reclaimSessionId]
335
380
  try {
@@ -349,7 +394,7 @@ function sessionIds(proof: LegacyProof): string[] {
349
394
  }
350
395
 
351
396
  function contextMatches(
352
- proof: LegacyProof,
397
+ proof: ReclaimRequestProof,
353
398
  expected?: Record<string, unknown>,
354
399
  ): boolean {
355
400
  if(!expected) {