@reclaimprotocol/client 0.1.28 → 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.
- package/README.md +255 -234
- package/lib/api.d.ts +2 -0
- package/lib/api.js +2 -0
- package/lib/client.js +2 -0
- package/lib/enckeys.d.ts +1 -1
- package/lib/enckeys.js +2 -2
- package/lib/encoding.d.ts +4 -0
- package/lib/encoding.js +10 -0
- package/lib/feature-flags.d.ts +9 -0
- package/lib/feature-flags.js +19 -0
- package/lib/index.d.ts +4 -4
- package/lib/index.js +4 -4
- package/lib/jwks.d.ts +8 -8
- package/lib/jwks.js +38 -17
- package/lib/launch/destination.d.ts +14 -0
- package/lib/launch/destination.js +89 -0
- package/lib/launch/device.d.ts +30 -0
- package/lib/launch/device.js +168 -0
- package/lib/launch/environment.d.ts +224 -0
- package/lib/launch/environment.js +98 -0
- package/lib/launch/errors.d.ts +20 -0
- package/lib/launch/errors.js +25 -0
- package/lib/launch/extension.d.ts +42 -0
- package/lib/launch/extension.js +72 -0
- package/lib/launch/flow.d.ts +82 -0
- package/lib/launch/flow.js +222 -0
- package/lib/launch/index.d.ts +16 -0
- package/lib/launch/index.js +12 -0
- package/lib/launch/mobile.d.ts +77 -0
- package/lib/launch/mobile.js +124 -0
- package/lib/launch/modal.d.ts +50 -0
- package/lib/launch/modal.js +258 -0
- package/lib/launch/options.d.ts +74 -0
- package/lib/launch/options.js +20 -0
- package/lib/launch/portal.d.ts +28 -0
- package/lib/launch/portal.js +68 -0
- package/lib/launch/qr.d.ts +5 -0
- package/lib/launch/qr.js +28 -0
- package/lib/launch/target.d.ts +46 -0
- package/lib/launch/target.js +128 -0
- package/lib/launch/vendor/qrcodegen.d.ts +134 -0
- package/lib/launch/vendor/qrcodegen.js +822 -0
- package/lib/legacy-proof.d.ts +2 -0
- package/lib/legacy-proof.js +37 -7
- package/lib/proof-hash.d.ts +5 -5
- package/lib/proof-hash.js +1 -1
- package/lib/proof-recipe.d.ts +3 -3
- package/lib/proof-recipe.js +1 -1
- package/lib/proof-types.d.ts +35 -8
- package/lib/reclaim.d.ts +81 -70
- package/lib/reclaim.js +79 -130
- package/lib/request-proof.d.ts +60 -0
- package/lib/request-proof.js +241 -0
- package/lib/result.d.ts +46 -38
- package/lib/result.js +160 -100
- package/lib/routes.gen.d.ts +76 -28
- package/lib/routes.gen.js +24 -11
- package/lib/tee-binding.d.ts +1 -1
- package/lib/types/openapi.d.ts +1 -0
- package/lib/types/openapi.gen.d.ts +1594 -488
- package/lib/types/openapi.gen.js +5 -0
- package/lib/verification-clients.d.ts +28 -14
- package/lib/verification-clients.js +31 -11
- package/lib/verify-attestor-tee.d.ts +19 -2
- package/lib/verify-attestor-tee.js +40 -4
- package/lib/verify-tee.d.ts +10 -1
- package/lib/verify-tee.js +34 -10
- package/package.json +5 -1
- package/src/api.ts +9 -0
- package/src/client.ts +2 -0
- package/src/enckeys.ts +2 -2
- package/src/encoding.ts +12 -0
- package/src/feature-flags.ts +39 -0
- package/src/index.ts +13 -14
- package/src/jwks.ts +52 -30
- package/src/launch/destination.ts +111 -0
- package/src/launch/device.ts +242 -0
- package/src/launch/environment.ts +337 -0
- package/src/launch/errors.ts +49 -0
- package/src/launch/extension.ts +124 -0
- package/src/launch/flow.ts +369 -0
- package/src/launch/index.ts +42 -0
- package/src/launch/mobile.ts +184 -0
- package/src/launch/modal.ts +378 -0
- package/src/launch/options.ts +99 -0
- package/src/launch/portal.ts +104 -0
- package/src/launch/qr.ts +37 -0
- package/src/launch/target.ts +150 -0
- package/src/launch/vendor/qrcodegen.ts +1034 -0
- package/src/proof-hash.ts +6 -6
- package/src/proof-recipe.ts +3 -3
- package/src/proof-types.ts +36 -8
- package/src/reclaim.ts +185 -218
- package/src/{legacy-proof.ts → request-proof.ts} +81 -36
- package/src/result.ts +238 -172
- package/src/routes.gen.ts +24 -11
- package/src/tee-binding.ts +1 -1
- package/src/types/openapi.gen.ts +1599 -488
- package/src/types/openapi.ts +3 -1
- package/src/verification-clients.ts +33 -19
- package/src/verify-attestor-tee.ts +66 -8
- package/src/verify-tee.ts +44 -13
- package/lib/api-client.d.ts +0 -31
- package/lib/api-client.js +0 -112
- package/lib/api-consts.d.ts +0 -4
- package/lib/api-consts.js +0 -4
- package/lib/api-errors.d.ts +0 -13
- package/lib/api-errors.js +0 -30
- package/lib/eip712.d.ts +0 -20
- package/lib/eip712.js +0 -108
package/src/reclaim.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Declarative entry point for the consumer SDK. The ONLY config is the org
|
|
3
3
|
* secret (`rorg_…`) — it authenticates every call and identifies the org
|
|
4
|
-
* server-side.
|
|
5
|
-
*
|
|
6
|
-
* `results.decrypt`/`receive` (only when results arrive encrypted):
|
|
4
|
+
* server-side. Pass the optional organization ETH private key only to the
|
|
5
|
+
* operation that needs it:
|
|
7
6
|
*
|
|
8
|
-
* const reclaim =
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* const reclaim = ReclaimVerification.create({ orgSecret })
|
|
8
|
+
* const session = await reclaim.sessions.create({
|
|
9
|
+
* providers,
|
|
10
|
+
* context,
|
|
11
|
+
* orgEthPrivateKey, // optional TEE session binding
|
|
12
|
+
* })
|
|
14
13
|
* // …end-user completes session.verificationUrl…
|
|
15
14
|
* const outcome = await reclaim.results.receive(callbackBody, {
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* orgEthPrivateKey, // optional decryption and TEE proof verification
|
|
16
|
+
* reclaimSessionId: session.id,
|
|
18
17
|
* })
|
|
19
18
|
*
|
|
20
19
|
* Built by composition over the package's pure helpers (`decryptCallback`,
|
|
@@ -28,10 +27,9 @@ import {
|
|
|
28
27
|
type DecryptionKeyInput,
|
|
29
28
|
loadDecryptionKey,
|
|
30
29
|
} from './enckeys.ts'
|
|
31
|
-
import { isProblemError } from './errors.ts'
|
|
32
30
|
import type { BuilderProofRecipe } from './proof-recipe.ts'
|
|
33
31
|
import { proofHashesFromBuilderRecipe } from './proof-recipe.ts'
|
|
34
|
-
import type {
|
|
32
|
+
import type { ReclaimRequestProof } from './proof-types.ts'
|
|
35
33
|
import {
|
|
36
34
|
type CallbackBody,
|
|
37
35
|
decryptCallback,
|
|
@@ -45,21 +43,9 @@ import {
|
|
|
45
43
|
type LegacyTeeNonceMode,
|
|
46
44
|
} from './tee-binding.ts'
|
|
47
45
|
|
|
48
|
-
type CallbackSubscription = components['schemas']['CallbackSubscription']
|
|
49
46
|
type VerificationSession = components['schemas']['VerificationSession']
|
|
50
47
|
type VerificationEvent = components['schemas']['VerificationEvent']
|
|
51
48
|
type VerificationEventRecord = components['schemas']['VerificationEventRecord']
|
|
52
|
-
type OrgKeypair = components['schemas']['OrgKeypair']
|
|
53
|
-
type OrgKeypairRequest = components['schemas']['OrgKeypairRequest']
|
|
54
|
-
|
|
55
|
-
/** Terminal lifecycle events selected by default for callback subscriptions. */
|
|
56
|
-
export const TERMINAL_EVENTS = [
|
|
57
|
-
'verification_success',
|
|
58
|
-
'verification_rejected',
|
|
59
|
-
'verification_error',
|
|
60
|
-
'verification_cancelled',
|
|
61
|
-
'session_expired',
|
|
62
|
-
] as const
|
|
63
49
|
|
|
64
50
|
const SIGNED_RESULT_EVENTS = [
|
|
65
51
|
'verification_success',
|
|
@@ -67,47 +53,41 @@ const SIGNED_RESULT_EVENTS = [
|
|
|
67
53
|
'verification_error',
|
|
68
54
|
] as const
|
|
69
55
|
|
|
70
|
-
export interface RegisterCallbackInput {
|
|
71
|
-
callbackUrl: string
|
|
72
|
-
events: VerificationEvent[]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
56
|
export interface CreateSessionInput {
|
|
76
57
|
/** Providers to verify, in run order. The end-user proves each (one browser
|
|
77
58
|
* session) and one combined result is delivered; all are required. */
|
|
78
59
|
providers: { providerId: string, version?: string }[]
|
|
79
60
|
context: Record<string, unknown>
|
|
80
|
-
/** Exact registered Verification Client base URL.
|
|
61
|
+
/** Exact registered Verification Client base URL. Defaults to the Portal.
|
|
62
|
+
* It may contain a
|
|
81
63
|
* `{sessionId}` placeholder; Builder otherwise adds the session id as a
|
|
82
64
|
* query parameter. Open the returned `verificationUrl`, not this base
|
|
83
65
|
* URL. */
|
|
84
|
-
verificationClientUrl
|
|
85
|
-
/** Optional legacy-compatible TEE session binding. The
|
|
86
|
-
*
|
|
66
|
+
verificationClientUrl?: string
|
|
67
|
+
/** Optional legacy-compatible TEE session binding. The organization's ETH
|
|
68
|
+
* private key never leaves this process.
|
|
87
69
|
* Session creation returns only after Builder stores the signed binding. */
|
|
88
|
-
|
|
70
|
+
orgEthPrivateKey?: string
|
|
71
|
+
/** Override the legacy TEE nonce mode when binding a session. */
|
|
72
|
+
teeNonceMode?: LegacyTeeNonceMode
|
|
73
|
+
/** Opt this session into diagnostic mode: more event detail, including
|
|
74
|
+
* personal data such as the claimant's IP addresses. Builder builds
|
|
75
|
+
* `verificationUrl` with `diag=1` for a diagnostic session — do not
|
|
76
|
+
* append `diag` to the URL directly, since Builder strips it and derives
|
|
77
|
+
* the parameter from this flag instead. Treat a diagnostic
|
|
78
|
+
* `verificationUrl` as sensitive. */
|
|
79
|
+
diagnostics?: boolean
|
|
89
80
|
}
|
|
90
81
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
appSecret: string
|
|
82
|
+
/** Input for explicitly binding a session's legacy-compatible TEE context. */
|
|
83
|
+
export interface BindSessionTeeNonceInput {
|
|
84
|
+
orgEthPrivateKey: string
|
|
95
85
|
timestamp?: string
|
|
96
86
|
/** `hash` is the default. Use `signature` only for an older Verification
|
|
97
87
|
* Client that expects the 65-byte EIP-191 nonce. */
|
|
98
|
-
|
|
88
|
+
teeNonceMode?: LegacyTeeNonceMode
|
|
99
89
|
}
|
|
100
90
|
|
|
101
|
-
/** Input for `keypair.set`: the org's eth public key as uncompressed hex
|
|
102
|
-
* (`publicKey`, the form wallets/ethers emit) OR as a secp256k1 JWK
|
|
103
|
-
* (`publicKeyJwk`); keep the matching eth private key to decrypt results.
|
|
104
|
-
* `canEncryptResult` toggles ECIES encryption (default off). The two request
|
|
105
|
-
* shapes are the spec's `OrgKeypairRequest` union members, relaxed to optional
|
|
106
|
-
* so the SDK caller supplies just one key form. */
|
|
107
|
-
export type SetKeypairInput =
|
|
108
|
-
& Partial<components['schemas']['OrgKeypairHexRequest']>
|
|
109
|
-
& Partial<components['schemas']['OrgKeypairJwkRequest']>
|
|
110
|
-
|
|
111
91
|
export interface ReclaimConfig {
|
|
112
92
|
/** The org secret (`rorg_…`). Authenticates ALL API calls and identifies
|
|
113
93
|
* the org server-side. The only required config. */
|
|
@@ -120,70 +100,82 @@ export interface ReclaimConfig {
|
|
|
120
100
|
headers?: Record<string, string>
|
|
121
101
|
}
|
|
122
102
|
|
|
103
|
+
/** Serializable SDK configuration.
|
|
104
|
+
* Credentials and runtime hooks are omitted. */
|
|
105
|
+
export interface ReclaimVerificationState {
|
|
106
|
+
version: 1
|
|
107
|
+
baseUrl: string
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Runtime-only values required to restore serialized SDK configuration. */
|
|
111
|
+
export type ImportReclaimVerificationConfig = Omit<ReclaimConfig, 'baseUrl'>
|
|
112
|
+
|
|
123
113
|
/** Options for `results.receive` — the verification options plus an optional
|
|
124
114
|
* eth decryption key (the org's eth private key as `0x`-hex). The key is
|
|
125
115
|
* needed only when the delivery is an ECIES ciphertext. */
|
|
126
|
-
export type ReceiveOptions = VerifyResultFullOptions
|
|
127
|
-
|
|
116
|
+
export type ReceiveOptions = VerifyResultFullOptions
|
|
117
|
+
|
|
118
|
+
/** A thin backend-side handle on a session's `verificationUrl` — see
|
|
119
|
+
* `reclaim.flow`. Not the browser launcher: this holds no device
|
|
120
|
+
* detection or launch mechanism, only the URL the browser needs. The
|
|
121
|
+
* browser-only `ReclaimFlow.from(session)` in
|
|
122
|
+
* `@reclaimprotocol/client/launch` is the counterpart that actually
|
|
123
|
+
* resolves how to open it. */
|
|
124
|
+
export interface SessionFlow {
|
|
125
|
+
/** `session.verificationUrl`. A `Promise` so backend code calling this
|
|
126
|
+
* doesn't need to special-case it against the browser `ReclaimFlow`'s
|
|
127
|
+
* `getUrl()`, which does real async work. */
|
|
128
|
+
getUrl(): Promise<string>
|
|
129
|
+
readonly session: VerificationSession
|
|
128
130
|
}
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
/** `reclaim.flow(session)` wraps a session already created via
|
|
133
|
+
* `sessions.create`/`sessions.get`; `reclaim.flow.create(input)` creates one
|
|
134
|
+
* and wraps it in the same step. */
|
|
135
|
+
export interface ReclaimFlowFactory {
|
|
136
|
+
(session: VerificationSession): SessionFlow
|
|
137
|
+
create(input: CreateSessionInput): Promise<SessionFlow>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface ReclaimVerification {
|
|
141
|
+
/** Serialize non-secret configuration. Credentials, headers, and custom
|
|
142
|
+
* fetch implementations are never included. */
|
|
143
|
+
export(): string
|
|
131
144
|
/** Typed escape hatch: `reclaim.api('OperationId', args)` for any spec
|
|
132
145
|
* operation. */
|
|
133
146
|
api: ReclaimClient['call']
|
|
134
147
|
sessions: {
|
|
135
148
|
/** Create a verification session. `verificationClientUrl` defaults to
|
|
136
|
-
* the
|
|
137
|
-
create(
|
|
138
|
-
input: Omit<CreateSessionInput, 'verificationClientUrl'> &
|
|
139
|
-
Partial<Pick<CreateSessionInput, 'verificationClientUrl'>>,
|
|
140
|
-
): Promise<VerificationSession>
|
|
149
|
+
* the Portal, `VerificationClient.portals`. */
|
|
150
|
+
create(input: CreateSessionInput): Promise<VerificationSession>
|
|
141
151
|
/** Add the immutable legacy-compatible TEE context after session
|
|
142
152
|
* creation. Normally use
|
|
143
|
-
* `sessions.create({
|
|
153
|
+
* `sessions.create({ orgEthPrivateKey })`; this method
|
|
144
154
|
* supports retrying or explicitly orchestrating the two-step flow. */
|
|
145
155
|
bindTeeNonce(
|
|
146
|
-
|
|
147
|
-
input:
|
|
156
|
+
reclaimSessionId: string,
|
|
157
|
+
input: BindSessionTeeNonceInput,
|
|
148
158
|
): Promise<VerificationSession>
|
|
149
|
-
get(
|
|
150
|
-
listEvents(
|
|
151
|
-
}
|
|
152
|
-
callbacks: {
|
|
153
|
-
/** Register a callback subscription (one-time setup). Delivery is scoped
|
|
154
|
-
* to the token's org; results are ECIES-encrypted to the org's eth key
|
|
155
|
-
* when `canEncryptResult` is enabled (see `keypair.set`), else plaintext.
|
|
156
|
-
* `events` defaults to all terminal lifecycle events. */
|
|
157
|
-
register(
|
|
158
|
-
input: Partial<RegisterCallbackInput> &
|
|
159
|
-
Pick<RegisterCallbackInput, 'callbackUrl'>,
|
|
160
|
-
): Promise<CallbackSubscription>
|
|
161
|
-
list(): Promise<CallbackSubscription[]>
|
|
162
|
-
remove(subscriptionId: string): Promise<void>
|
|
163
|
-
}
|
|
164
|
-
keypair: {
|
|
165
|
-
/** Set/rotate org `orgId`'s single eth encryption keypair. When
|
|
166
|
-
* `canEncryptResult` is enabled, terminal results are delivered as an
|
|
167
|
-
* ECIES ciphertext encrypted to it. */
|
|
168
|
-
set(orgId: string, input: SetKeypairInput): Promise<OrgKeypair>
|
|
169
|
-
/** Get org `orgId`'s encryption keypair, or `undefined` when none is set
|
|
170
|
-
* (the org is keyless ⇒ plaintext delivery). */
|
|
171
|
-
get(orgId: string): Promise<OrgKeypair | undefined>
|
|
159
|
+
get(reclaimSessionId: string): Promise<VerificationSession>
|
|
160
|
+
listEvents(reclaimSessionId: string): Promise<VerificationEventRecord[]>
|
|
172
161
|
}
|
|
173
162
|
results: {
|
|
174
163
|
/** Decrypt a callback body (ECIES ciphertext or plaintext JWS) to the
|
|
175
|
-
* signed JWS. Pass
|
|
176
|
-
*
|
|
177
|
-
decrypt(
|
|
164
|
+
* signed JWS. Pass `orgEthPrivateKey` only when the delivery is
|
|
165
|
+
* encrypted. */
|
|
166
|
+
decrypt(
|
|
167
|
+
body: CallbackBody,
|
|
168
|
+
orgEthPrivateKey?: DecryptionKeyInput,
|
|
169
|
+
): Promise<Jws>
|
|
178
170
|
/** Verify a decrypted JWS: JWKS discovery, ES256K signature,
|
|
179
|
-
* `reclaimSessionId`/`aud`, any legacy `exp`, and exact
|
|
171
|
+
* `reclaimSessionId`/`aud`, any legacy `exp`, and exact proofs. */
|
|
180
172
|
verify(
|
|
181
173
|
jws: Jws,
|
|
182
174
|
opts?: VerifyResultFullOptions,
|
|
183
175
|
): Promise<VerifyResultFullOutcome>
|
|
184
176
|
/** Handle any callback delivery in one call — the 90% path. Branches on
|
|
185
177
|
* the envelope's `event`: a terminal event is decrypted (with
|
|
186
|
-
* `opts.
|
|
178
|
+
* `opts.orgEthPrivateKey` when encrypted) and verified into a `result`; a
|
|
187
179
|
* non-terminal event is returned as a plaintext (unsigned) `event`
|
|
188
180
|
* notification without decryption. (A raw JWE/JWS body with no envelope
|
|
189
181
|
* `event` is treated as a terminal result.) */
|
|
@@ -192,6 +184,8 @@ export interface Reclaim {
|
|
|
192
184
|
opts?: ReceiveOptions,
|
|
193
185
|
): Promise<ReceivedDelivery>
|
|
194
186
|
}
|
|
187
|
+
/** `reclaim.flow(session)` — see `ReclaimFlowFactory`. */
|
|
188
|
+
flow: ReclaimFlowFactory
|
|
195
189
|
}
|
|
196
190
|
|
|
197
191
|
/** Outcome of `results.receive` — a verified terminal result or a plaintext
|
|
@@ -204,11 +198,26 @@ export type ReceivedDelivery =
|
|
|
204
198
|
eventData: Record<string, unknown>
|
|
205
199
|
}
|
|
206
200
|
|
|
207
|
-
export
|
|
201
|
+
export const ReclaimVerification = Object.freeze({
|
|
202
|
+
create: createReclaimVerification,
|
|
203
|
+
import: importReclaimVerification,
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
function importReclaimVerification(
|
|
207
|
+
serialized: string,
|
|
208
|
+
config: ImportReclaimVerificationConfig,
|
|
209
|
+
) {
|
|
210
|
+
const state = parseReclaimVerificationState(serialized)
|
|
211
|
+
return createReclaimVerification({ ...config, baseUrl: state.baseUrl })
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function createReclaimVerification(
|
|
215
|
+
config: ReclaimConfig,
|
|
216
|
+
): ReclaimVerification {
|
|
208
217
|
if(!config?.orgSecret) {
|
|
209
218
|
throw new Error(
|
|
210
|
-
'
|
|
211
|
-
+ '
|
|
219
|
+
'ReclaimVerification.create requires an org secret — pass '
|
|
220
|
+
+ 'ReclaimVerification.create({ orgSecret: <rorg_… org secret> })',
|
|
212
221
|
)
|
|
213
222
|
}
|
|
214
223
|
|
|
@@ -228,101 +237,42 @@ export function createReclaim(config: ReclaimConfig): Reclaim {
|
|
|
228
237
|
)
|
|
229
238
|
|
|
230
239
|
return {
|
|
240
|
+
export() {
|
|
241
|
+
return JSON.stringify({
|
|
242
|
+
version: 1,
|
|
243
|
+
baseUrl: client.baseUrl,
|
|
244
|
+
} satisfies ReclaimVerificationState)
|
|
245
|
+
},
|
|
231
246
|
api: client.call.bind(client),
|
|
232
247
|
sessions: {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const body:
|
|
236
|
-
components['schemas']['CreateVerificationSessionRequest'] = {
|
|
237
|
-
...request,
|
|
238
|
-
verificationClientUrl:
|
|
239
|
-
request.verificationClientUrl ?? `${client.baseUrl}/v/{sessionId}`,
|
|
240
|
-
}
|
|
241
|
-
const { data } = await client.call('CreateVerificationSession', {
|
|
242
|
-
body,
|
|
243
|
-
})
|
|
244
|
-
return teeAttestation
|
|
245
|
-
? bindTeeNonce(data, teeAttestation)
|
|
246
|
-
: data
|
|
247
|
-
},
|
|
248
|
-
async bindTeeNonce(sessionId, input) {
|
|
248
|
+
create: createSession,
|
|
249
|
+
async bindTeeNonce(reclaimSessionId, input) {
|
|
249
250
|
const { data } = await client.call('GetVerificationSession', {
|
|
250
|
-
params: { sessionId },
|
|
251
|
+
params: { sessionId: reclaimSessionId },
|
|
251
252
|
})
|
|
252
253
|
return bindTeeNonce(data, input)
|
|
253
254
|
},
|
|
254
|
-
async get(
|
|
255
|
+
async get(reclaimSessionId) {
|
|
255
256
|
const { data } = await client.call('GetVerificationSession', {
|
|
256
|
-
params: { sessionId },
|
|
257
|
+
params: { sessionId: reclaimSessionId },
|
|
257
258
|
})
|
|
258
259
|
return data
|
|
259
260
|
},
|
|
260
|
-
async listEvents(
|
|
261
|
+
async listEvents(reclaimSessionId) {
|
|
261
262
|
const { data } = await client.call('ListVerificationEvents', {
|
|
262
|
-
params: { sessionId },
|
|
263
|
-
})
|
|
264
|
-
return data.items
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
callbacks: {
|
|
268
|
-
async register(input) {
|
|
269
|
-
const events = input.events ?? [...TERMINAL_EVENTS]
|
|
270
|
-
const { data } = await client.call('CreateCallbackSubscription', {
|
|
271
|
-
body: { callbackUrl: input.callbackUrl, events },
|
|
263
|
+
params: { sessionId: reclaimSessionId },
|
|
272
264
|
})
|
|
273
|
-
return data
|
|
274
|
-
},
|
|
275
|
-
async list() {
|
|
276
|
-
const { data } = await client.call('ListCallbackSubscriptions')
|
|
277
265
|
return data.items
|
|
278
266
|
},
|
|
279
|
-
async remove(subscriptionId) {
|
|
280
|
-
await client.call('DeleteCallbackSubscription', {
|
|
281
|
-
params: { subscriptionId },
|
|
282
|
-
})
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
keypair: {
|
|
286
|
-
async set(orgId, input) {
|
|
287
|
-
// Forward the eth public key as given (hex or JWK); the API stores
|
|
288
|
-
// the secp256k1 JWK + derived address.
|
|
289
|
-
const body = {
|
|
290
|
-
...(input.publicKeyJwk
|
|
291
|
-
? { publicKeyJwk: input.publicKeyJwk }
|
|
292
|
-
: { publicKey: input.publicKey }),
|
|
293
|
-
canEncryptResult: input.canEncryptResult ?? false,
|
|
294
|
-
...(input.label ? { label: input.label } : {}),
|
|
295
|
-
} as OrgKeypairRequest
|
|
296
|
-
const { data } = await client.call('SetOrgKeypair', {
|
|
297
|
-
params: { orgId },
|
|
298
|
-
body,
|
|
299
|
-
})
|
|
300
|
-
return data
|
|
301
|
-
},
|
|
302
|
-
async get(orgId) {
|
|
303
|
-
try {
|
|
304
|
-
const { data } = await client.call('GetOrgKeypair', {
|
|
305
|
-
params: { orgId },
|
|
306
|
-
})
|
|
307
|
-
return data
|
|
308
|
-
} catch(err) {
|
|
309
|
-
// 404 ⇒ keyless org (plaintext delivery).
|
|
310
|
-
if(isNotFound(err)) {
|
|
311
|
-
return undefined
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
throw err
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
267
|
},
|
|
318
268
|
results: {
|
|
319
|
-
async decrypt(body,
|
|
320
|
-
return decryptCallback(resolveKey(
|
|
269
|
+
async decrypt(body, orgEthPrivateKey) {
|
|
270
|
+
return decryptCallback(resolveKey(orgEthPrivateKey), body)
|
|
321
271
|
},
|
|
322
272
|
verify(jws, opts) {
|
|
323
273
|
return verifyResultFull(jws, {
|
|
324
274
|
fetchImpl: config.fetch,
|
|
325
|
-
...
|
|
275
|
+
...withBuilderTrust(opts),
|
|
326
276
|
})
|
|
327
277
|
},
|
|
328
278
|
async receive(body, opts) {
|
|
@@ -348,28 +298,59 @@ export function createReclaim(config: ReclaimConfig): Reclaim {
|
|
|
348
298
|
}
|
|
349
299
|
}
|
|
350
300
|
|
|
351
|
-
const {
|
|
352
|
-
const key = resolveKey(
|
|
301
|
+
const { orgEthPrivateKey, ...verifyOpts } = opts ?? {}
|
|
302
|
+
const key = resolveKey(orgEthPrivateKey)
|
|
353
303
|
const jws = await decryptCallback(key, body)
|
|
354
304
|
const result = await verifyResultFull(jws, {
|
|
355
305
|
fetchImpl: config.fetch,
|
|
356
|
-
...
|
|
306
|
+
...withBuilderTrust({ ...verifyOpts, orgEthPrivateKey }),
|
|
357
307
|
})
|
|
358
308
|
return { kind: 'result', result }
|
|
359
309
|
},
|
|
360
310
|
},
|
|
311
|
+
flow: Object.assign(
|
|
312
|
+
(session: VerificationSession): SessionFlow => makeSessionFlow(session),
|
|
313
|
+
{
|
|
314
|
+
async create(input: CreateSessionInput): Promise<SessionFlow> {
|
|
315
|
+
return makeSessionFlow(await createSession(input))
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
),
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function createSession(
|
|
322
|
+
input: CreateSessionInput,
|
|
323
|
+
): Promise<VerificationSession> {
|
|
324
|
+
const {
|
|
325
|
+
orgEthPrivateKey,
|
|
326
|
+
teeNonceMode,
|
|
327
|
+
...request
|
|
328
|
+
} = input
|
|
329
|
+
const body:
|
|
330
|
+
components['schemas']['CreateVerificationSessionRequest'] = {
|
|
331
|
+
...request,
|
|
332
|
+
}
|
|
333
|
+
const { data } = await client.call('CreateVerificationSession', {
|
|
334
|
+
body,
|
|
335
|
+
})
|
|
336
|
+
return orgEthPrivateKey
|
|
337
|
+
? bindTeeNonce(data, {
|
|
338
|
+
orgEthPrivateKey,
|
|
339
|
+
teeNonceMode,
|
|
340
|
+
})
|
|
341
|
+
: data
|
|
361
342
|
}
|
|
362
343
|
|
|
363
344
|
async function bindTeeNonce(
|
|
364
345
|
session: VerificationSession,
|
|
365
|
-
input:
|
|
346
|
+
input: BindSessionTeeNonceInput,
|
|
366
347
|
) {
|
|
367
348
|
const binding = createBuilderTeeBinding({
|
|
368
349
|
organizationId: session.orgId,
|
|
369
|
-
organizationVerificationPrivateKey: input.
|
|
350
|
+
organizationVerificationPrivateKey: input.orgEthPrivateKey,
|
|
370
351
|
sessionId: session.id,
|
|
371
352
|
timestamp: input.timestamp ?? Date.now().toString(),
|
|
372
|
-
nonceMode: input.
|
|
353
|
+
nonceMode: input.teeNonceMode,
|
|
373
354
|
})
|
|
374
355
|
const { data } = await client.call('BindVerificationSessionTeeNonce', {
|
|
375
356
|
params: { sessionId: session.id },
|
|
@@ -394,7 +375,7 @@ export function createReclaim(config: ReclaimConfig): Reclaim {
|
|
|
394
375
|
providerId: string,
|
|
395
376
|
providerVersion: string | undefined,
|
|
396
377
|
_allowedTags: string[] | undefined,
|
|
397
|
-
proofs:
|
|
378
|
+
proofs: ReclaimRequestProof[],
|
|
398
379
|
) {
|
|
399
380
|
let exactVersion = providerVersion
|
|
400
381
|
if(!exactVersion) {
|
|
@@ -422,10 +403,9 @@ export function createReclaim(config: ReclaimConfig): Reclaim {
|
|
|
422
403
|
}]
|
|
423
404
|
}
|
|
424
405
|
|
|
425
|
-
function
|
|
426
|
-
const
|
|
406
|
+
function withBuilderTrust(opts: VerifyResultFullOptions = {}) {
|
|
407
|
+
const reclaimSessionId = opts.reclaimSessionId
|
|
427
408
|
const requestedProofValidation = opts.proofValidation
|
|
428
|
-
?? opts.proofVerification
|
|
429
409
|
const resolvedOpts: VerifyResultFullOptions = {
|
|
430
410
|
...opts,
|
|
431
411
|
proofValidation: {
|
|
@@ -433,59 +413,32 @@ export function createReclaim(config: ReclaimConfig): Reclaim {
|
|
|
433
413
|
resolveProvider: requestedProofValidation?.resolveProvider
|
|
434
414
|
?? resolvedProvider,
|
|
435
415
|
},
|
|
436
|
-
...(
|
|
416
|
+
...(reclaimSessionId && !opts.resolveSession && !opts.proofRecipes
|
|
437
417
|
? { resolveSession: resolvedSession }
|
|
438
418
|
: {}),
|
|
439
419
|
}
|
|
440
420
|
if(
|
|
441
421
|
opts.signerPublicKey
|
|
442
|
-
|| opts.issuer
|
|
443
|
-
|| opts.trustedIssuer
|
|
444
|
-
|| opts.issuers?.length
|
|
445
422
|
|| opts.trustedIssuers?.length
|
|
446
|
-
|| opts.resolveIssuers
|
|
447
|
-
|| opts.issuerResolver
|
|
448
|
-
|| opts.issuersByVerificationClientId
|
|
449
423
|
|| opts.jwksUrl
|
|
450
|
-
|| opts.jwksUrlsByVerificationClientId
|
|
451
|
-
|| opts.resolveJwksUrl
|
|
452
424
|
) {
|
|
453
425
|
return resolvedOpts
|
|
454
426
|
}
|
|
455
427
|
|
|
456
428
|
return {
|
|
457
429
|
...resolvedOpts,
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if(!session) {
|
|
461
|
-
return []
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return session.verificationClientIssuer
|
|
465
|
-
? [session.verificationClientIssuer]
|
|
466
|
-
: []
|
|
467
|
-
},
|
|
468
|
-
resolveJwksUrl: async(verificationClientId: string) => {
|
|
469
|
-
const session = await registeredClient(verificationClientId)
|
|
470
|
-
return session?.verificationClientJwksUrl
|
|
471
|
-
},
|
|
430
|
+
trustedIssuers: [client.baseUrl],
|
|
431
|
+
jwksUrl: `${client.baseUrl}/.well-known/jwks.json`,
|
|
472
432
|
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
473
435
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
'GetVerificationSession',
|
|
481
|
-
{ params: { sessionId } },
|
|
482
|
-
)
|
|
483
|
-
if(session.verificationClientId !== verificationClientId) {
|
|
484
|
-
return undefined
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
return session
|
|
488
|
-
}
|
|
436
|
+
function makeSessionFlow(session: VerificationSession): SessionFlow {
|
|
437
|
+
return {
|
|
438
|
+
session,
|
|
439
|
+
async getUrl() {
|
|
440
|
+
return session.verificationUrl
|
|
441
|
+
},
|
|
489
442
|
}
|
|
490
443
|
}
|
|
491
444
|
|
|
@@ -516,7 +469,21 @@ function parseEventData(data: string | undefined): Record<string, unknown> {
|
|
|
516
469
|
}
|
|
517
470
|
}
|
|
518
471
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
472
|
+
function parseReclaimVerificationState(
|
|
473
|
+
serialized: string,
|
|
474
|
+
): ReclaimVerificationState {
|
|
475
|
+
const state: unknown = JSON.parse(serialized)
|
|
476
|
+
if(
|
|
477
|
+
!state
|
|
478
|
+
|| typeof state !== 'object'
|
|
479
|
+
|| Array.isArray(state)
|
|
480
|
+
|| !('version' in state)
|
|
481
|
+
|| state.version !== 1
|
|
482
|
+
|| !('baseUrl' in state)
|
|
483
|
+
|| typeof state.baseUrl !== 'string'
|
|
484
|
+
) {
|
|
485
|
+
throw new Error('Invalid ReclaimVerification state')
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return { version: 1, baseUrl: state.baseUrl }
|
|
522
489
|
}
|