kentucky-signer-viem 0.1.1 → 0.1.4
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 +207 -219
- package/dist/index.d.mts +802 -7
- package/dist/index.d.ts +802 -7
- package/dist/index.js +964 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +955 -37
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +61 -3
- package/dist/react/index.d.ts +61 -3
- package/dist/react/index.js +1286 -173
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1288 -174
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/account.ts +111 -22
- package/src/auth.ts +16 -6
- package/src/client.ts +438 -18
- package/src/ephemeral.ts +407 -0
- package/src/index.ts +56 -0
- package/src/react/context.tsx +360 -45
- package/src/react/hooks.ts +11 -0
- package/src/react/index.ts +1 -0
- package/src/secure-client.ts +417 -0
- package/src/types.ts +332 -0
package/src/ephemeral.ts
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ephemeral Key Management for Kentucky Signer
|
|
3
|
+
*
|
|
4
|
+
* Implements ephemeral ECDSA keys for request signing.
|
|
5
|
+
* The ephemeral public key is bound to the JWT token during authentication,
|
|
6
|
+
* and the private key is used to sign all subsequent request payloads.
|
|
7
|
+
*
|
|
8
|
+
* Security Properties:
|
|
9
|
+
* - Ephemeral keys are generated fresh for each session
|
|
10
|
+
* - Private keys never leave the client
|
|
11
|
+
* - SGX verifies payload signatures before processing
|
|
12
|
+
* - Prevents replay attacks and token theft
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { base64UrlEncode, base64UrlDecode } from './utils'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Ephemeral key pair for request signing
|
|
19
|
+
*/
|
|
20
|
+
export interface EphemeralKeyPair {
|
|
21
|
+
/** Public key in SPKI format (base64url encoded) */
|
|
22
|
+
publicKey: string
|
|
23
|
+
/** Private CryptoKey for signing (not exportable) */
|
|
24
|
+
privateKey: CryptoKey
|
|
25
|
+
/** Algorithm used (ES256 = P-256 with SHA-256) */
|
|
26
|
+
algorithm: 'ES256'
|
|
27
|
+
/** Creation timestamp */
|
|
28
|
+
createdAt: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Signed request payload
|
|
33
|
+
*/
|
|
34
|
+
export interface SignedPayload {
|
|
35
|
+
/** Original payload (JSON string) */
|
|
36
|
+
payload: string
|
|
37
|
+
/** Signature (base64url encoded) */
|
|
38
|
+
signature: string
|
|
39
|
+
/** Timestamp when signature was created */
|
|
40
|
+
timestamp: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if WebCrypto is available
|
|
45
|
+
*/
|
|
46
|
+
export function isWebCryptoAvailable(): boolean {
|
|
47
|
+
return (
|
|
48
|
+
typeof crypto !== 'undefined' &&
|
|
49
|
+
typeof crypto.subtle !== 'undefined' &&
|
|
50
|
+
typeof crypto.getRandomValues !== 'undefined'
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate an ephemeral ECDSA key pair using WebCrypto
|
|
56
|
+
*
|
|
57
|
+
* Uses P-256 curve (secp256r1) with SHA-256 for ES256 signatures.
|
|
58
|
+
*
|
|
59
|
+
* @returns Generated key pair
|
|
60
|
+
* @throws Error if WebCrypto is not available
|
|
61
|
+
*/
|
|
62
|
+
export async function generateEphemeralKeyPair(): Promise<EphemeralKeyPair> {
|
|
63
|
+
if (!isWebCryptoAvailable()) {
|
|
64
|
+
throw new Error('WebCrypto is not available in this environment')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Generate ECDSA key pair on P-256 curve
|
|
68
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
69
|
+
{
|
|
70
|
+
name: 'ECDSA',
|
|
71
|
+
namedCurve: 'P-256',
|
|
72
|
+
},
|
|
73
|
+
true, // extractable (only for public key export)
|
|
74
|
+
['sign', 'verify']
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
// Export public key in SPKI format
|
|
78
|
+
const publicKeyBuffer = await crypto.subtle.exportKey('spki', keyPair.publicKey)
|
|
79
|
+
const publicKeyBase64 = base64UrlEncode(new Uint8Array(publicKeyBuffer))
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
publicKey: publicKeyBase64,
|
|
83
|
+
privateKey: keyPair.privateKey,
|
|
84
|
+
algorithm: 'ES256',
|
|
85
|
+
createdAt: Date.now(),
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sign a request payload with the ephemeral private key
|
|
91
|
+
*
|
|
92
|
+
* The signature covers:
|
|
93
|
+
* - The JSON payload string
|
|
94
|
+
* - A timestamp to prevent replay attacks
|
|
95
|
+
*
|
|
96
|
+
* @param payload - Request payload (will be JSON stringified if object)
|
|
97
|
+
* @param keyPair - Ephemeral key pair
|
|
98
|
+
* @returns Signed payload with signature
|
|
99
|
+
*/
|
|
100
|
+
export async function signPayload(
|
|
101
|
+
payload: string | object,
|
|
102
|
+
keyPair: EphemeralKeyPair
|
|
103
|
+
): Promise<SignedPayload> {
|
|
104
|
+
const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload)
|
|
105
|
+
const timestamp = Date.now()
|
|
106
|
+
|
|
107
|
+
// Create message to sign: timestamp + payload
|
|
108
|
+
// This prevents replay attacks
|
|
109
|
+
const message = `${timestamp}.${payloadString}`
|
|
110
|
+
const messageBytes = new TextEncoder().encode(message)
|
|
111
|
+
|
|
112
|
+
// Sign with ECDSA P-256 + SHA-256
|
|
113
|
+
const signatureBuffer = await crypto.subtle.sign(
|
|
114
|
+
{
|
|
115
|
+
name: 'ECDSA',
|
|
116
|
+
hash: 'SHA-256',
|
|
117
|
+
},
|
|
118
|
+
keyPair.privateKey,
|
|
119
|
+
messageBytes
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const signatureBase64 = base64UrlEncode(new Uint8Array(signatureBuffer))
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
payload: payloadString,
|
|
126
|
+
signature: signatureBase64,
|
|
127
|
+
timestamp,
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Verify a signed payload (for testing purposes)
|
|
133
|
+
*
|
|
134
|
+
* @param signedPayload - The signed payload to verify
|
|
135
|
+
* @param publicKeyBase64 - Public key in SPKI format (base64url)
|
|
136
|
+
* @returns True if signature is valid
|
|
137
|
+
*/
|
|
138
|
+
export async function verifyPayload(
|
|
139
|
+
signedPayload: SignedPayload,
|
|
140
|
+
publicKeyBase64: string
|
|
141
|
+
): Promise<boolean> {
|
|
142
|
+
try {
|
|
143
|
+
// Import public key
|
|
144
|
+
const publicKeyBytes = base64UrlDecode(publicKeyBase64)
|
|
145
|
+
const publicKey = await crypto.subtle.importKey(
|
|
146
|
+
'spki',
|
|
147
|
+
publicKeyBytes.buffer as ArrayBuffer,
|
|
148
|
+
{
|
|
149
|
+
name: 'ECDSA',
|
|
150
|
+
namedCurve: 'P-256',
|
|
151
|
+
},
|
|
152
|
+
false,
|
|
153
|
+
['verify']
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
// Reconstruct message
|
|
157
|
+
const message = `${signedPayload.timestamp}.${signedPayload.payload}`
|
|
158
|
+
const messageBytes = new TextEncoder().encode(message)
|
|
159
|
+
|
|
160
|
+
// Decode signature
|
|
161
|
+
const signatureBytes = base64UrlDecode(signedPayload.signature)
|
|
162
|
+
|
|
163
|
+
// Verify signature
|
|
164
|
+
return await crypto.subtle.verify(
|
|
165
|
+
{
|
|
166
|
+
name: 'ECDSA',
|
|
167
|
+
hash: 'SHA-256',
|
|
168
|
+
},
|
|
169
|
+
publicKey,
|
|
170
|
+
signatureBytes.buffer as ArrayBuffer,
|
|
171
|
+
messageBytes
|
|
172
|
+
)
|
|
173
|
+
} catch {
|
|
174
|
+
return false
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Storage key for ephemeral key pair
|
|
180
|
+
*/
|
|
181
|
+
const EPHEMERAL_KEY_STORAGE_KEY = 'kentucky_signer_ephemeral'
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Ephemeral key storage interface
|
|
185
|
+
*/
|
|
186
|
+
export interface EphemeralKeyStorage {
|
|
187
|
+
save(keyPair: EphemeralKeyPair): Promise<void>
|
|
188
|
+
load(): Promise<EphemeralKeyPair | null>
|
|
189
|
+
clear(): Promise<void>
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* In-memory ephemeral key storage
|
|
194
|
+
*
|
|
195
|
+
* Keys are lost when the page is refreshed.
|
|
196
|
+
*/
|
|
197
|
+
export class MemoryEphemeralKeyStorage implements EphemeralKeyStorage {
|
|
198
|
+
private keyPair: EphemeralKeyPair | null = null
|
|
199
|
+
|
|
200
|
+
async save(keyPair: EphemeralKeyPair): Promise<void> {
|
|
201
|
+
this.keyPair = keyPair
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async load(): Promise<EphemeralKeyPair | null> {
|
|
205
|
+
return this.keyPair
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async clear(): Promise<void> {
|
|
209
|
+
this.keyPair = null
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* IndexedDB ephemeral key storage
|
|
215
|
+
*
|
|
216
|
+
* Persists keys across page refreshes but not browser restarts.
|
|
217
|
+
* Uses non-extractable keys for security.
|
|
218
|
+
*/
|
|
219
|
+
export class IndexedDBEphemeralKeyStorage implements EphemeralKeyStorage {
|
|
220
|
+
private dbName = 'kentucky_signer_ephemeral_keys'
|
|
221
|
+
private storeName = 'keys'
|
|
222
|
+
|
|
223
|
+
private async getDB(): Promise<IDBDatabase> {
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
const request = indexedDB.open(this.dbName, 1)
|
|
226
|
+
|
|
227
|
+
request.onerror = () => reject(request.error)
|
|
228
|
+
request.onsuccess = () => resolve(request.result)
|
|
229
|
+
|
|
230
|
+
request.onupgradeneeded = () => {
|
|
231
|
+
const db = request.result
|
|
232
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
233
|
+
db.createObjectStore(this.storeName)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async save(keyPair: EphemeralKeyPair): Promise<void> {
|
|
240
|
+
const db = await this.getDB()
|
|
241
|
+
|
|
242
|
+
// Store the CryptoKey directly (IndexedDB supports structured clone of CryptoKey)
|
|
243
|
+
return new Promise((resolve, reject) => {
|
|
244
|
+
const tx = db.transaction(this.storeName, 'readwrite')
|
|
245
|
+
const store = tx.objectStore(this.storeName)
|
|
246
|
+
|
|
247
|
+
const data = {
|
|
248
|
+
publicKey: keyPair.publicKey,
|
|
249
|
+
privateKey: keyPair.privateKey,
|
|
250
|
+
algorithm: keyPair.algorithm,
|
|
251
|
+
createdAt: keyPair.createdAt,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const request = store.put(data, EPHEMERAL_KEY_STORAGE_KEY)
|
|
255
|
+
request.onerror = () => reject(request.error)
|
|
256
|
+
request.onsuccess = () => resolve()
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async load(): Promise<EphemeralKeyPair | null> {
|
|
261
|
+
const db = await this.getDB()
|
|
262
|
+
|
|
263
|
+
return new Promise((resolve, reject) => {
|
|
264
|
+
const tx = db.transaction(this.storeName, 'readonly')
|
|
265
|
+
const store = tx.objectStore(this.storeName)
|
|
266
|
+
|
|
267
|
+
const request = store.get(EPHEMERAL_KEY_STORAGE_KEY)
|
|
268
|
+
request.onerror = () => reject(request.error)
|
|
269
|
+
request.onsuccess = () => {
|
|
270
|
+
if (request.result) {
|
|
271
|
+
resolve({
|
|
272
|
+
publicKey: request.result.publicKey,
|
|
273
|
+
privateKey: request.result.privateKey,
|
|
274
|
+
algorithm: request.result.algorithm,
|
|
275
|
+
createdAt: request.result.createdAt,
|
|
276
|
+
})
|
|
277
|
+
} else {
|
|
278
|
+
resolve(null)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async clear(): Promise<void> {
|
|
285
|
+
const db = await this.getDB()
|
|
286
|
+
|
|
287
|
+
return new Promise((resolve, reject) => {
|
|
288
|
+
const tx = db.transaction(this.storeName, 'readwrite')
|
|
289
|
+
const store = tx.objectStore(this.storeName)
|
|
290
|
+
|
|
291
|
+
const request = store.delete(EPHEMERAL_KEY_STORAGE_KEY)
|
|
292
|
+
request.onerror = () => reject(request.error)
|
|
293
|
+
request.onsuccess = () => resolve()
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Create an ephemeral key manager for a session
|
|
300
|
+
*
|
|
301
|
+
* The manager handles:
|
|
302
|
+
* - Key generation on first use
|
|
303
|
+
* - Key storage and retrieval
|
|
304
|
+
* - Automatic key rotation (optional)
|
|
305
|
+
* - Storage migration (switching between IndexedDB and memory)
|
|
306
|
+
*/
|
|
307
|
+
export class EphemeralKeyManager {
|
|
308
|
+
private keyPair: EphemeralKeyPair | null = null
|
|
309
|
+
private storage: EphemeralKeyStorage
|
|
310
|
+
|
|
311
|
+
constructor(storage?: EphemeralKeyStorage) {
|
|
312
|
+
this.storage = storage ?? new MemoryEphemeralKeyStorage()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get or generate ephemeral key pair
|
|
317
|
+
*/
|
|
318
|
+
async getKeyPair(): Promise<EphemeralKeyPair> {
|
|
319
|
+
// Try to load from storage first
|
|
320
|
+
if (!this.keyPair) {
|
|
321
|
+
this.keyPair = await this.storage.load()
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Generate new key pair if not found
|
|
325
|
+
if (!this.keyPair) {
|
|
326
|
+
this.keyPair = await generateEphemeralKeyPair()
|
|
327
|
+
await this.storage.save(this.keyPair)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return this.keyPair
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get the public key for authentication
|
|
335
|
+
*/
|
|
336
|
+
async getPublicKey(): Promise<string> {
|
|
337
|
+
const keyPair = await this.getKeyPair()
|
|
338
|
+
return keyPair.publicKey
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Sign a request payload
|
|
343
|
+
*/
|
|
344
|
+
async signPayload(payload: string | object): Promise<SignedPayload> {
|
|
345
|
+
const keyPair = await this.getKeyPair()
|
|
346
|
+
return signPayload(payload, keyPair)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Rotate the key pair (generate new keys)
|
|
351
|
+
*/
|
|
352
|
+
async rotate(): Promise<EphemeralKeyPair> {
|
|
353
|
+
await this.storage.clear()
|
|
354
|
+
this.keyPair = await generateEphemeralKeyPair()
|
|
355
|
+
await this.storage.save(this.keyPair)
|
|
356
|
+
return this.keyPair
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Clear the key pair (logout)
|
|
361
|
+
*/
|
|
362
|
+
async clear(): Promise<void> {
|
|
363
|
+
await this.storage.clear()
|
|
364
|
+
this.keyPair = null
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Check if a key pair exists
|
|
369
|
+
*/
|
|
370
|
+
async hasKeyPair(): Promise<boolean> {
|
|
371
|
+
if (this.keyPair) return true
|
|
372
|
+
const loaded = await this.storage.load()
|
|
373
|
+
return loaded !== null
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Migrate key pair to a new storage backend
|
|
378
|
+
*
|
|
379
|
+
* This preserves the existing key pair while switching storage.
|
|
380
|
+
* The key is saved to the new storage and removed from the old storage.
|
|
381
|
+
*
|
|
382
|
+
* @param newStorage - The new storage backend to migrate to
|
|
383
|
+
*/
|
|
384
|
+
async migrateStorage(newStorage: EphemeralKeyStorage): Promise<void> {
|
|
385
|
+
// Get current key pair (from memory or old storage)
|
|
386
|
+
const currentKeyPair = this.keyPair ?? await this.storage.load()
|
|
387
|
+
|
|
388
|
+
// Clear old storage
|
|
389
|
+
await this.storage.clear()
|
|
390
|
+
|
|
391
|
+
// Switch to new storage
|
|
392
|
+
this.storage = newStorage
|
|
393
|
+
|
|
394
|
+
// Save key pair to new storage if we had one
|
|
395
|
+
if (currentKeyPair) {
|
|
396
|
+
await this.storage.save(currentKeyPair)
|
|
397
|
+
this.keyPair = currentKeyPair
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get the current storage backend
|
|
403
|
+
*/
|
|
404
|
+
getStorage(): EphemeralKeyStorage {
|
|
405
|
+
return this.storage
|
|
406
|
+
}
|
|
407
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,8 @@ export {
|
|
|
13
13
|
createServerAccount,
|
|
14
14
|
type KentuckySignerAccount,
|
|
15
15
|
type KentuckySignerAccountOptions,
|
|
16
|
+
type TwoFactorCodes,
|
|
17
|
+
type TwoFactorCallback,
|
|
16
18
|
} from './account'
|
|
17
19
|
|
|
18
20
|
// Client
|
|
@@ -22,6 +24,27 @@ export {
|
|
|
22
24
|
createClient,
|
|
23
25
|
} from './client'
|
|
24
26
|
|
|
27
|
+
// Secure Client (with ephemeral key signing)
|
|
28
|
+
export {
|
|
29
|
+
SecureKentuckySignerClient,
|
|
30
|
+
createSecureClient,
|
|
31
|
+
type SecureClientOptions,
|
|
32
|
+
} from './secure-client'
|
|
33
|
+
|
|
34
|
+
// Ephemeral Key Management
|
|
35
|
+
export {
|
|
36
|
+
generateEphemeralKeyPair,
|
|
37
|
+
signPayload,
|
|
38
|
+
verifyPayload,
|
|
39
|
+
isWebCryptoAvailable,
|
|
40
|
+
EphemeralKeyManager,
|
|
41
|
+
MemoryEphemeralKeyStorage,
|
|
42
|
+
IndexedDBEphemeralKeyStorage,
|
|
43
|
+
type EphemeralKeyPair,
|
|
44
|
+
type SignedPayload,
|
|
45
|
+
type EphemeralKeyStorage,
|
|
46
|
+
} from './ephemeral'
|
|
47
|
+
|
|
25
48
|
// Authentication
|
|
26
49
|
export {
|
|
27
50
|
authenticateWithPasskey,
|
|
@@ -43,6 +66,8 @@ export type {
|
|
|
43
66
|
ChallengeResponse,
|
|
44
67
|
AuthResponse,
|
|
45
68
|
AccountInfoResponse,
|
|
69
|
+
AccountInfoExtendedResponse,
|
|
70
|
+
AuthConfig,
|
|
46
71
|
EvmSignatureResponse,
|
|
47
72
|
ApiErrorResponse,
|
|
48
73
|
PasskeyCredential,
|
|
@@ -56,6 +81,37 @@ export type {
|
|
|
56
81
|
AccountCreationResponse,
|
|
57
82
|
CreatePasswordAccountRequest,
|
|
58
83
|
PasswordAuthRequest,
|
|
84
|
+
AddPasswordRequest,
|
|
85
|
+
AddPasswordResponse,
|
|
86
|
+
AddPasskeyRequest,
|
|
87
|
+
AddPasskeyResponse,
|
|
88
|
+
RemovePasskeyResponse,
|
|
89
|
+
AuthResponseWithEphemeral,
|
|
90
|
+
// Guardian types
|
|
91
|
+
GuardianInfo,
|
|
92
|
+
AddGuardianRequest,
|
|
93
|
+
AddGuardianResponse,
|
|
94
|
+
RemoveGuardianResponse,
|
|
95
|
+
GetGuardiansResponse,
|
|
96
|
+
// Recovery types
|
|
97
|
+
InitiateRecoveryRequest,
|
|
98
|
+
InitiateRecoveryResponse,
|
|
99
|
+
VerifyGuardianRequest,
|
|
100
|
+
VerifyGuardianResponse,
|
|
101
|
+
RecoveryStatusRequest,
|
|
102
|
+
RecoveryStatusResponse,
|
|
103
|
+
CompleteRecoveryRequest,
|
|
104
|
+
CompleteRecoveryResponse,
|
|
105
|
+
CancelRecoveryResponse,
|
|
106
|
+
// 2FA types
|
|
107
|
+
TwoFactorStatusResponse,
|
|
108
|
+
TotpSetupResponse,
|
|
109
|
+
TotpEnableRequest,
|
|
110
|
+
TwoFactorResponse,
|
|
111
|
+
TwoFactorVerifyResponse,
|
|
112
|
+
PinSetupRequest,
|
|
113
|
+
PinSetupResponse,
|
|
114
|
+
SignEvmRequestWith2FA,
|
|
59
115
|
} from './types'
|
|
60
116
|
|
|
61
117
|
// Utilities
|