@zerodev/wallet-core 0.0.1-alpha.18 → 0.0.1-alpha.20
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 +1 -1
- package/dist/_cjs/actions/auth/authenticateWithOAuth.js +2 -2
- package/dist/_cjs/actions/auth/authenticateWithOAuth.js.map +1 -1
- package/dist/_cjs/actions/auth/getOAuthLoginUrl.js +18 -0
- package/dist/_cjs/actions/auth/getOAuthLoginUrl.js.map +1 -0
- package/dist/_cjs/actions/auth/getWhoami.js +2 -2
- package/dist/_cjs/actions/auth/getWhoami.js.map +1 -1
- package/dist/_cjs/actions/auth/index.js +3 -1
- package/dist/_cjs/actions/auth/index.js.map +1 -1
- package/dist/_cjs/actions/auth/loginWithStamp.js +5 -5
- package/dist/_cjs/actions/auth/loginWithStamp.js.map +1 -1
- package/dist/_cjs/actions/index.js +2 -1
- package/dist/_cjs/actions/index.js.map +1 -1
- package/dist/_cjs/actions/wallet/signingUtils.js +2 -2
- package/dist/_cjs/actions/wallet/signingUtils.js.map +1 -1
- package/dist/_cjs/client/createClient.js +5 -5
- package/dist/_cjs/client/createClient.js.map +1 -1
- package/dist/_cjs/client/decorators/client.js +1 -0
- package/dist/_cjs/client/decorators/client.js.map +1 -1
- package/dist/_cjs/client/index.js.map +1 -1
- package/dist/_cjs/client/transports/createTransport.js +6 -5
- package/dist/_cjs/client/transports/createTransport.js.map +1 -1
- package/dist/_cjs/client/transports/rest.js +7 -7
- package/dist/_cjs/client/transports/rest.js.map +1 -1
- package/dist/_cjs/core/createZeroDevWallet.js +32 -61
- package/dist/_cjs/core/createZeroDevWallet.js.map +1 -1
- package/dist/_cjs/index.js.map +1 -1
- package/dist/_cjs/stampers/indexedDbStamper.js +20 -2
- package/dist/_cjs/stampers/indexedDbStamper.js.map +1 -1
- package/dist/_cjs/stampers/webauthnStamper.js +23 -3
- package/dist/_cjs/stampers/webauthnStamper.js.map +1 -1
- package/dist/_cjs/utils/exportPrivateKey.js +1 -1
- package/dist/_cjs/utils/exportPrivateKey.js.map +1 -1
- package/dist/_cjs/utils/exportWallet.js +2 -6
- package/dist/_cjs/utils/exportWallet.js.map +1 -1
- package/dist/_cjs/utils/hpke.js +4 -15
- package/dist/_cjs/utils/hpke.js.map +1 -1
- package/dist/_cjs/utils/utils.js +5 -6
- package/dist/_cjs/utils/utils.js.map +1 -1
- package/dist/_esm/actions/auth/authenticateWithOAuth.js +3 -2
- package/dist/_esm/actions/auth/authenticateWithOAuth.js.map +1 -1
- package/dist/_esm/actions/auth/getOAuthLoginUrl.js +23 -0
- package/dist/_esm/actions/auth/getOAuthLoginUrl.js.map +1 -0
- package/dist/_esm/actions/auth/getWhoami.js +2 -2
- package/dist/_esm/actions/auth/getWhoami.js.map +1 -1
- package/dist/_esm/actions/auth/index.js +1 -0
- package/dist/_esm/actions/auth/index.js.map +1 -1
- package/dist/_esm/actions/auth/loginWithStamp.js +5 -5
- package/dist/_esm/actions/auth/loginWithStamp.js.map +1 -1
- package/dist/_esm/actions/index.js +1 -1
- package/dist/_esm/actions/index.js.map +1 -1
- package/dist/_esm/actions/wallet/signingUtils.js +2 -2
- package/dist/_esm/actions/wallet/signingUtils.js.map +1 -1
- package/dist/_esm/client/createClient.js +5 -5
- package/dist/_esm/client/createClient.js.map +1 -1
- package/dist/_esm/client/decorators/client.js +2 -1
- package/dist/_esm/client/decorators/client.js.map +1 -1
- package/dist/_esm/client/index.js +1 -1
- package/dist/_esm/client/index.js.map +1 -1
- package/dist/_esm/client/transports/createTransport.js +6 -5
- package/dist/_esm/client/transports/createTransport.js.map +1 -1
- package/dist/_esm/client/transports/rest.js +7 -7
- package/dist/_esm/client/transports/rest.js.map +1 -1
- package/dist/_esm/core/createZeroDevWallet.js +33 -62
- package/dist/_esm/core/createZeroDevWallet.js.map +1 -1
- package/dist/_esm/index.js.map +1 -1
- package/dist/_esm/stampers/indexedDbStamper.js +20 -2
- package/dist/_esm/stampers/indexedDbStamper.js.map +1 -1
- package/dist/_esm/stampers/webauthnStamper.js +23 -4
- package/dist/_esm/stampers/webauthnStamper.js.map +1 -1
- package/dist/_esm/utils/exportPrivateKey.js +1 -1
- package/dist/_esm/utils/exportPrivateKey.js.map +1 -1
- package/dist/_esm/utils/exportWallet.js +2 -6
- package/dist/_esm/utils/exportWallet.js.map +1 -1
- package/dist/_esm/utils/hpke.js +6 -22
- package/dist/_esm/utils/hpke.js.map +1 -1
- package/dist/_esm/utils/utils.js +5 -6
- package/dist/_esm/utils/utils.js.map +1 -1
- package/dist/_types/actions/auth/authenticateWithOAuth.d.ts +3 -0
- package/dist/_types/actions/auth/authenticateWithOAuth.d.ts.map +1 -1
- package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts +30 -0
- package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts.map +1 -0
- package/dist/_types/actions/auth/index.d.ts +1 -0
- package/dist/_types/actions/auth/index.d.ts.map +1 -1
- package/dist/_types/actions/auth/loginWithStamp.d.ts +2 -1
- package/dist/_types/actions/auth/loginWithStamp.d.ts.map +1 -1
- package/dist/_types/actions/index.d.ts +1 -1
- package/dist/_types/actions/index.d.ts.map +1 -1
- package/dist/_types/client/decorators/client.d.ts +7 -1
- package/dist/_types/client/decorators/client.d.ts.map +1 -1
- package/dist/_types/client/index.d.ts +1 -1
- package/dist/_types/client/index.d.ts.map +1 -1
- package/dist/_types/client/transports/createTransport.d.ts +2 -0
- package/dist/_types/client/transports/createTransport.d.ts.map +1 -1
- package/dist/_types/client/transports/rest.d.ts +5 -4
- package/dist/_types/client/transports/rest.d.ts.map +1 -1
- package/dist/_types/client/types.d.ts +9 -9
- package/dist/_types/client/types.d.ts.map +1 -1
- package/dist/_types/core/createZeroDevWallet.d.ts +5 -1
- package/dist/_types/core/createZeroDevWallet.d.ts.map +1 -1
- package/dist/_types/index.d.ts +2 -2
- package/dist/_types/index.d.ts.map +1 -1
- package/dist/_types/stampers/index.d.ts +1 -1
- package/dist/_types/stampers/index.d.ts.map +1 -1
- package/dist/_types/stampers/indexedDbStamper.d.ts +2 -2
- package/dist/_types/stampers/indexedDbStamper.d.ts.map +1 -1
- package/dist/_types/stampers/types.d.ts +36 -5
- package/dist/_types/stampers/types.d.ts.map +1 -1
- package/dist/_types/stampers/webauthnStamper.d.ts +2 -2
- package/dist/_types/stampers/webauthnStamper.d.ts.map +1 -1
- package/dist/_types/types/session.d.ts +2 -3
- package/dist/_types/types/session.d.ts.map +1 -1
- package/dist/_types/utils/buildClientSignature.d.ts +3 -3
- package/dist/_types/utils/buildClientSignature.d.ts.map +1 -1
- package/dist/_types/utils/exportWallet.d.ts.map +1 -1
- package/dist/_types/utils/hpke.d.ts.map +1 -1
- package/dist/_types/utils/utils.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/actions/auth/authenticateWithOAuth.ts +5 -2
- package/src/actions/auth/getOAuthLoginUrl.ts +48 -0
- package/src/actions/auth/getWhoami.ts +2 -2
- package/src/actions/auth/index.ts +5 -0
- package/src/actions/auth/loginWithStamp.ts +7 -6
- package/src/actions/index.ts +3 -0
- package/src/actions/wallet/signingUtils.ts +2 -2
- package/src/client/createClient.ts +6 -6
- package/src/client/decorators/client.ts +13 -0
- package/src/client/index.ts +4 -1
- package/src/client/transports/createTransport.ts +8 -5
- package/src/client/transports/rest.ts +13 -12
- package/src/client/types.ts +9 -9
- package/src/core/createZeroDevWallet.ts +40 -77
- package/src/index.ts +11 -3
- package/src/stampers/index.ts +2 -2
- package/src/stampers/indexedDbStamper.ts +27 -4
- package/src/stampers/types.ts +38 -5
- package/src/stampers/webauthnStamper.ts +27 -6
- package/src/types/session.ts +2 -3
- package/src/utils/buildClientSignature.ts +3 -3
- package/src/utils/exportPrivateKey.ts +1 -1
- package/src/utils/exportWallet.ts +2 -6
- package/src/utils/hpke.ts +7 -33
- package/src/utils/utils.ts +5 -6
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getWebAuthnAttestation } from '@turnkey/http'
|
|
2
1
|
import type { LocalAccount } from 'viem/accounts'
|
|
3
2
|
import type {
|
|
4
3
|
EmailCustomization,
|
|
@@ -6,6 +5,7 @@ import type {
|
|
|
6
5
|
} from '../actions/auth/index.js'
|
|
7
6
|
import { toViemAccount } from '../adapters/viem.js'
|
|
8
7
|
import {
|
|
8
|
+
type CreateTransportOptions,
|
|
9
9
|
createAuthProxyClient,
|
|
10
10
|
createClient,
|
|
11
11
|
type ZeroDevWalletClient,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
KMS_SERVER_URL,
|
|
18
18
|
} from '../constants.js'
|
|
19
19
|
import { createIndexedDbStamper } from '../stampers/indexedDbStamper.js'
|
|
20
|
+
import type { ApiKeyStamper, PasskeyStamper } from '../stampers/types.js'
|
|
20
21
|
import { createWebauthnStamper } from '../stampers/webauthnStamper.js'
|
|
21
22
|
import { createWebStorageAdapter } from '../storage/adapters.js'
|
|
22
23
|
import {
|
|
@@ -26,19 +27,16 @@ import {
|
|
|
26
27
|
import { SessionType, type ZeroDevWalletSession } from '../types/session.js'
|
|
27
28
|
import { buildClientSignature } from '../utils/buildClientSignature.js'
|
|
28
29
|
import { encryptOtpAttempt } from '../utils/encryptOtpAttempt.js'
|
|
29
|
-
import {
|
|
30
|
-
base64UrlEncode,
|
|
31
|
-
generateCompressedPublicKeyFromKeyPair,
|
|
32
|
-
generateRandomBuffer,
|
|
33
|
-
humanReadableDateTime,
|
|
34
|
-
parseSession,
|
|
35
|
-
} from '../utils/utils.js'
|
|
30
|
+
import { humanReadableDateTime, parseSession } from '../utils/utils.js'
|
|
36
31
|
export interface ZeroDevWalletConfig {
|
|
37
32
|
organizationId?: string
|
|
38
33
|
proxyBaseUrl?: string
|
|
39
34
|
projectId: string
|
|
40
35
|
sessionStorage?: StorageAdapter
|
|
41
36
|
rpId?: string
|
|
37
|
+
apiKeyStamper?: ApiKeyStamper
|
|
38
|
+
passkeyStamper?: PasskeyStamper
|
|
39
|
+
fetchOptions?: CreateTransportOptions['fetchOptions']
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
// Re-export EmailCustomization for convenience
|
|
@@ -134,15 +132,17 @@ export async function createZeroDevWallet(
|
|
|
134
132
|
sessionStorage || createWebStorageAdapter(),
|
|
135
133
|
)
|
|
136
134
|
|
|
137
|
-
const
|
|
135
|
+
const apiKeyStamper = config.apiKeyStamper ?? (await createIndexedDbStamper())
|
|
138
136
|
|
|
139
|
-
const
|
|
137
|
+
const passkeyStamper =
|
|
138
|
+
config.passkeyStamper ?? (await createWebauthnStamper({ rpId }))
|
|
140
139
|
|
|
141
140
|
const client = createClient({
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
apiKeyStamper,
|
|
142
|
+
passkeyStamper,
|
|
144
143
|
transport: zeroDevWalletTransport({
|
|
145
144
|
baseUrl: config.proxyBaseUrl || `${KMS_SERVER_URL}/api/v1`,
|
|
145
|
+
...(config.fetchOptions && { fetchOptions: config.fetchOptions }),
|
|
146
146
|
}),
|
|
147
147
|
})
|
|
148
148
|
|
|
@@ -151,8 +151,8 @@ export async function createZeroDevWallet(
|
|
|
151
151
|
return {
|
|
152
152
|
client,
|
|
153
153
|
async getPublicKey() {
|
|
154
|
-
await client.
|
|
155
|
-
const compressedPublicKey = await client.
|
|
154
|
+
await client.apiKeyStamper.resetKeyPair()
|
|
155
|
+
const compressedPublicKey = await client.apiKeyStamper.getPublicKey()
|
|
156
156
|
return compressedPublicKey
|
|
157
157
|
},
|
|
158
158
|
|
|
@@ -191,30 +191,22 @@ export async function createZeroDevWallet(
|
|
|
191
191
|
if (!activeSession) {
|
|
192
192
|
throw new Error('No active session')
|
|
193
193
|
}
|
|
194
|
-
if (activeSession.stamperType === '
|
|
195
|
-
const newKeyPair = await crypto.subtle.generateKey(
|
|
196
|
-
{
|
|
197
|
-
name: 'ECDSA',
|
|
198
|
-
namedCurve: 'P-256',
|
|
199
|
-
},
|
|
200
|
-
false,
|
|
201
|
-
['sign', 'verify'],
|
|
202
|
-
)
|
|
194
|
+
if (activeSession.stamperType === 'apiKey') {
|
|
203
195
|
const compressedPublicKeyHex =
|
|
204
|
-
await
|
|
196
|
+
await client.apiKeyStamper.prepareKeyRotation()
|
|
205
197
|
const data = await client.loginWithStamp({
|
|
206
198
|
targetPublicKey: compressedPublicKeyHex,
|
|
207
199
|
projectId,
|
|
208
200
|
organizationId: activeSession.organizationId,
|
|
209
|
-
stampWith: '
|
|
201
|
+
stampWith: 'apiKey',
|
|
210
202
|
})
|
|
211
|
-
await client.
|
|
203
|
+
await client.apiKeyStamper.commitKeyRotation()
|
|
212
204
|
const parsedSession = parseSession(data.session)
|
|
213
205
|
const session: ZeroDevWalletSession = {
|
|
214
206
|
id: `session_indexedDb_${Date.now()}`,
|
|
215
207
|
userId: parsedSession.userId,
|
|
216
208
|
organizationId: parsedSession.organizationId,
|
|
217
|
-
stamperType: '
|
|
209
|
+
stamperType: 'apiKey',
|
|
218
210
|
sessionType: SessionType.READ_WRITE,
|
|
219
211
|
token: data.session,
|
|
220
212
|
expiry: parsedSession.expiry,
|
|
@@ -231,26 +223,26 @@ export async function createZeroDevWallet(
|
|
|
231
223
|
async auth(params: AuthParams) {
|
|
232
224
|
switch (params.type) {
|
|
233
225
|
case 'oauth': {
|
|
226
|
+
const popSignature = await client.apiKeyStamper.sign(params.sessionId)
|
|
234
227
|
const data = await client.authenticateWithOAuth({
|
|
235
228
|
provider: params.provider,
|
|
236
229
|
projectId,
|
|
237
230
|
sessionId: params.sessionId,
|
|
231
|
+
popSignature,
|
|
238
232
|
})
|
|
239
233
|
|
|
240
234
|
if (data.session) {
|
|
241
235
|
// Parse the JWT to get session data
|
|
242
236
|
const parsedSession = parseSession(data.session)
|
|
243
|
-
const publicKey = await client.indexedDbStamper.getPublicKey()
|
|
244
237
|
const session: ZeroDevWalletSession = {
|
|
245
238
|
id: `session_oauth_${Date.now()}`,
|
|
246
239
|
userId: parsedSession.userId,
|
|
247
240
|
organizationId: parsedSession.organizationId,
|
|
248
|
-
stamperType: '
|
|
241
|
+
stamperType: 'apiKey',
|
|
249
242
|
sessionType: parsedSession.sessionType || SessionType.READ_WRITE,
|
|
250
243
|
token: data.session,
|
|
251
244
|
expiry: parsedSession.expiry,
|
|
252
245
|
createdAt: Date.now(),
|
|
253
|
-
publicKey: publicKey || '',
|
|
254
246
|
}
|
|
255
247
|
await sessionStorageManager.storeSession(session, session.id)
|
|
256
248
|
}
|
|
@@ -263,62 +255,35 @@ export async function createZeroDevWallet(
|
|
|
263
255
|
'mode' in params &&
|
|
264
256
|
params.mode === 'register'
|
|
265
257
|
) {
|
|
266
|
-
await client.
|
|
267
|
-
const tempPublicKey = await client.
|
|
258
|
+
await client.apiKeyStamper.resetKeyPair()
|
|
259
|
+
const tempPublicKey = await client.apiKeyStamper.getPublicKey()
|
|
268
260
|
if (!tempPublicKey) {
|
|
269
261
|
throw new Error('Failed to get public key')
|
|
270
262
|
}
|
|
271
|
-
const challenge = generateRandomBuffer()
|
|
272
|
-
const encodedChallenge = base64UrlEncode(challenge)
|
|
273
|
-
const authenticatorUserId = generateRandomBuffer()
|
|
274
263
|
const name = `ZeroDevWallet-${humanReadableDateTime()}`
|
|
275
|
-
const attestation
|
|
276
|
-
|
|
264
|
+
const { attestation, encodedChallenge } =
|
|
265
|
+
await passkeyStamper.register({
|
|
277
266
|
rp: { id: rpId, name: '' },
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
{
|
|
281
|
-
type: 'public-key',
|
|
282
|
-
alg: -7,
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
type: 'public-key',
|
|
286
|
-
alg: -257,
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
user: {
|
|
290
|
-
id: authenticatorUserId,
|
|
291
|
-
name,
|
|
292
|
-
displayName: name,
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
})
|
|
267
|
+
userName: name,
|
|
268
|
+
})
|
|
296
269
|
const data = await client.registerWithPasskey({
|
|
297
270
|
attestation,
|
|
298
271
|
challenge: encodedChallenge,
|
|
299
272
|
projectId,
|
|
300
273
|
encodedPublicKey: tempPublicKey,
|
|
301
274
|
})
|
|
302
|
-
const newKeyPair = await crypto.subtle.generateKey(
|
|
303
|
-
{
|
|
304
|
-
name: 'ECDSA',
|
|
305
|
-
namedCurve: 'P-256',
|
|
306
|
-
},
|
|
307
|
-
false,
|
|
308
|
-
['sign', 'verify'],
|
|
309
|
-
)
|
|
310
275
|
const compressedPublicKeyHex =
|
|
311
|
-
await
|
|
276
|
+
await client.apiKeyStamper.prepareKeyRotation()
|
|
312
277
|
const loginData = await client.loginWithStamp({
|
|
313
278
|
projectId,
|
|
314
279
|
targetPublicKey: compressedPublicKeyHex,
|
|
315
280
|
organizationId: data.subOrganizationId,
|
|
316
281
|
})
|
|
317
|
-
await client.
|
|
282
|
+
await client.apiKeyStamper.commitKeyRotation()
|
|
318
283
|
const parsedSession = parseSession(loginData.session)
|
|
319
284
|
const session: ZeroDevWalletSession = {
|
|
320
285
|
id: `session_indexedDb_${Date.now()}`,
|
|
321
|
-
stamperType: '
|
|
286
|
+
stamperType: 'apiKey',
|
|
322
287
|
createdAt: Date.now(),
|
|
323
288
|
sessionType: SessionType.READ_WRITE,
|
|
324
289
|
userId: parsedSession.userId,
|
|
@@ -336,9 +301,8 @@ export async function createZeroDevWallet(
|
|
|
336
301
|
'mode' in params &&
|
|
337
302
|
params.mode === 'login'
|
|
338
303
|
) {
|
|
339
|
-
await client.
|
|
340
|
-
const generatedPublicKey =
|
|
341
|
-
await client.indexedDbStamper.getPublicKey()
|
|
304
|
+
await client.apiKeyStamper.resetKeyPair()
|
|
305
|
+
const generatedPublicKey = await client.apiKeyStamper.getPublicKey()
|
|
342
306
|
if (!generatedPublicKey) {
|
|
343
307
|
throw new Error('Failed to get public key')
|
|
344
308
|
}
|
|
@@ -346,12 +310,12 @@ export async function createZeroDevWallet(
|
|
|
346
310
|
targetPublicKey: generatedPublicKey,
|
|
347
311
|
projectId,
|
|
348
312
|
organizationId,
|
|
349
|
-
stampWith: '
|
|
313
|
+
stampWith: 'passkey',
|
|
350
314
|
})
|
|
351
315
|
const parsedSession = parseSession(loginData.session)
|
|
352
316
|
const session: ZeroDevWalletSession = {
|
|
353
317
|
id: `session_indexedDb_${Date.now()}`,
|
|
354
|
-
stamperType: '
|
|
318
|
+
stamperType: 'apiKey',
|
|
355
319
|
createdAt: Date.now(),
|
|
356
320
|
sessionType: SessionType.READ_WRITE,
|
|
357
321
|
userId: parsedSession.userId,
|
|
@@ -416,8 +380,8 @@ export async function createZeroDevWallet(
|
|
|
416
380
|
const { otpId, otpCode, otpEncryptionTargetBundle } = otpParams
|
|
417
381
|
|
|
418
382
|
// Step 1: Generate new key pair
|
|
419
|
-
await client.
|
|
420
|
-
const targetPublicKey = await client.
|
|
383
|
+
await client.apiKeyStamper.resetKeyPair()
|
|
384
|
+
const targetPublicKey = await client.apiKeyStamper.getPublicKey()
|
|
421
385
|
|
|
422
386
|
if (!targetPublicKey) {
|
|
423
387
|
throw new Error('Failed to get public key')
|
|
@@ -449,7 +413,7 @@ export async function createZeroDevWallet(
|
|
|
449
413
|
const clientSignature = await buildClientSignature({
|
|
450
414
|
verificationToken,
|
|
451
415
|
publicKey: targetPublicKey,
|
|
452
|
-
stamper: client.
|
|
416
|
+
stamper: client.apiKeyStamper,
|
|
453
417
|
})
|
|
454
418
|
|
|
455
419
|
// Step 4: Login via backend (not Auth Proxy!)
|
|
@@ -466,13 +430,12 @@ export async function createZeroDevWallet(
|
|
|
466
430
|
id: `session_otp_${Date.now()}`,
|
|
467
431
|
userId: parsedSession.userId,
|
|
468
432
|
organizationId: parsedSession.organizationId,
|
|
469
|
-
stamperType: '
|
|
433
|
+
stamperType: 'apiKey',
|
|
470
434
|
sessionType:
|
|
471
435
|
parsedSession.sessionType || SessionType.READ_WRITE,
|
|
472
436
|
token: data.session,
|
|
473
437
|
expiry: parsedSession.expiry,
|
|
474
438
|
createdAt: Date.now(),
|
|
475
|
-
publicKey: targetPublicKey,
|
|
476
439
|
}
|
|
477
440
|
await sessionStorageManager.storeSession(session, session.id)
|
|
478
441
|
}
|
|
@@ -488,7 +451,7 @@ export async function createZeroDevWallet(
|
|
|
488
451
|
|
|
489
452
|
async logout() {
|
|
490
453
|
await sessionStorageManager.clearAllSessions()
|
|
491
|
-
await client.
|
|
454
|
+
await client.apiKeyStamper.resetKeyPair()
|
|
492
455
|
return true
|
|
493
456
|
},
|
|
494
457
|
|
package/src/index.ts
CHANGED
|
@@ -56,7 +56,12 @@ export { toViemAccount } from './adapters/viem.js'
|
|
|
56
56
|
export type { ZeroDevWalletActions } from './client/decorators/client.js'
|
|
57
57
|
// Client decorators
|
|
58
58
|
export { zeroDevWalletActions } from './client/decorators/client.js'
|
|
59
|
-
export type {
|
|
59
|
+
export type {
|
|
60
|
+
Client,
|
|
61
|
+
ClientConfig,
|
|
62
|
+
CreateTransportOptions,
|
|
63
|
+
Transport,
|
|
64
|
+
} from './client/index.js'
|
|
60
65
|
// Client
|
|
61
66
|
export {
|
|
62
67
|
createBaseClient,
|
|
@@ -80,9 +85,12 @@ export {
|
|
|
80
85
|
createWebauthnStamper,
|
|
81
86
|
} from './stampers/index.js'
|
|
82
87
|
export type {
|
|
88
|
+
ApiKeyStamper,
|
|
89
|
+
Attestation,
|
|
83
90
|
IframeStamper,
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
PasskeyRegistrationOptions,
|
|
92
|
+
PasskeyRegistrationResult,
|
|
93
|
+
PasskeyStamper,
|
|
86
94
|
} from './stampers/types.js'
|
|
87
95
|
// Storage
|
|
88
96
|
export type { StorageAdapter, StorageManager } from './storage/manager.js'
|
package/src/stampers/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { createIframeStamper } from './iframeStamper.js'
|
|
2
2
|
export { createIndexedDbStamper } from './indexedDbStamper.js'
|
|
3
3
|
export type {
|
|
4
|
+
ApiKeyStamper,
|
|
4
5
|
IframeStamper,
|
|
5
|
-
|
|
6
|
-
WebauthnStamper,
|
|
6
|
+
PasskeyStamper,
|
|
7
7
|
} from './types.js'
|
|
8
8
|
export { createWebauthnStamper } from './webauthnStamper.js'
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { IndexedDbStamper as TurnkeyIndexedDbStamper } from '@turnkey/indexed-db-stamper'
|
|
2
|
-
import
|
|
2
|
+
import { generateCompressedPublicKeyFromKeyPair } from '../utils/utils.js'
|
|
3
|
+
import type { ApiKeyStamper } from './types.js'
|
|
3
4
|
|
|
4
|
-
export async function createIndexedDbStamper(): Promise<
|
|
5
|
+
export async function createIndexedDbStamper(): Promise<ApiKeyStamper> {
|
|
5
6
|
const inner = new TurnkeyIndexedDbStamper()
|
|
6
7
|
await inner.init()
|
|
7
8
|
|
|
9
|
+
let pendingKeyPair: CryptoKeyPair | null = null
|
|
10
|
+
|
|
8
11
|
return {
|
|
9
12
|
async getPublicKey() {
|
|
10
13
|
return await inner.getPublicKey()
|
|
@@ -12,11 +15,31 @@ export async function createIndexedDbStamper(): Promise<IndexedDbStamper> {
|
|
|
12
15
|
async stamp(payload: string) {
|
|
13
16
|
return await inner.stamp(payload)
|
|
14
17
|
},
|
|
18
|
+
async sign(payload: string) {
|
|
19
|
+
return await inner.sign(payload)
|
|
20
|
+
},
|
|
15
21
|
async clear() {
|
|
16
22
|
await inner.clear()
|
|
17
23
|
},
|
|
18
|
-
async resetKeyPair(
|
|
19
|
-
|
|
24
|
+
async resetKeyPair() {
|
|
25
|
+
pendingKeyPair = null
|
|
26
|
+
await inner.resetKeyPair()
|
|
27
|
+
},
|
|
28
|
+
async prepareKeyRotation() {
|
|
29
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
30
|
+
{ name: 'ECDSA', namedCurve: 'P-256' },
|
|
31
|
+
false,
|
|
32
|
+
['sign', 'verify'],
|
|
33
|
+
)
|
|
34
|
+
pendingKeyPair = keyPair
|
|
35
|
+
return await generateCompressedPublicKeyFromKeyPair(keyPair)
|
|
36
|
+
},
|
|
37
|
+
async commitKeyRotation() {
|
|
38
|
+
if (!pendingKeyPair) {
|
|
39
|
+
throw new Error('No pending key rotation to commit')
|
|
40
|
+
}
|
|
41
|
+
await inner.resetKeyPair(pendingKeyPair)
|
|
42
|
+
pendingKeyPair = null
|
|
20
43
|
},
|
|
21
44
|
}
|
|
22
45
|
}
|
package/src/stampers/types.ts
CHANGED
|
@@ -5,8 +5,6 @@ export type Stamp = {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export type Stamper = {
|
|
8
|
-
/** retrieve public key compressed or otherwise as per the stamper */
|
|
9
|
-
getPublicKey: () => Promise<string | null>
|
|
10
8
|
/** produce Turnkey header value for a given request body */
|
|
11
9
|
stamp: (payload: string) => Promise<Stamp>
|
|
12
10
|
/** clear local state (embedded key, IDB keypair, etc.) */
|
|
@@ -16,6 +14,8 @@ export type Stamper = {
|
|
|
16
14
|
export type KeyFormat = 'Hexadecimal' | 'Solana'
|
|
17
15
|
|
|
18
16
|
export type IframeStamper = Stamper & {
|
|
17
|
+
/** retrieve public key compressed or otherwise as per the stamper */
|
|
18
|
+
getPublicKey: () => Promise<string | null>
|
|
19
19
|
init(): Promise<string>
|
|
20
20
|
injectCredentialBundle(bundle: string): Promise<boolean>
|
|
21
21
|
injectWalletExportBundle(
|
|
@@ -30,7 +30,40 @@ export type IframeStamper = Stamper & {
|
|
|
30
30
|
applySettings(settings: { styles?: Record<string, string> }): Promise<boolean>
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export type
|
|
34
|
-
|
|
33
|
+
export type ApiKeyStamper = Stamper & {
|
|
34
|
+
/** retrieve public key compressed or otherwise as per the stamper */
|
|
35
|
+
getPublicKey: () => Promise<string | null>
|
|
36
|
+
/** Generate + activate a new key pair immediately (simple cases: login init, logout). */
|
|
37
|
+
resetKeyPair: () => Promise<void>
|
|
38
|
+
/** Generate a new key pair internally, return its compressed public key, but keep the OLD key active for stamp(). */
|
|
39
|
+
prepareKeyRotation: () => Promise<string>
|
|
40
|
+
/** Promote the pending key to active. Call after the server accepts the new key. */
|
|
41
|
+
commitKeyRotation: () => Promise<void>
|
|
42
|
+
/**
|
|
43
|
+
* Sign `payload` with the currently active key. Returns a hex-encoded
|
|
44
|
+
* ECDSA-P256 / SHA-256 signature in ASN.1 DER form.
|
|
45
|
+
*/
|
|
46
|
+
sign: (payload: string) => Promise<string>
|
|
47
|
+
}
|
|
48
|
+
export type Attestation = {
|
|
49
|
+
attestationObject: string
|
|
50
|
+
clientDataJson: string
|
|
51
|
+
credentialId: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type PasskeyRegistrationOptions = {
|
|
55
|
+
rp: { id: string; name: string }
|
|
56
|
+
userName: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type PasskeyRegistrationResult = {
|
|
60
|
+
attestation: Attestation
|
|
61
|
+
encodedChallenge: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type PasskeyStamper = Stamper & {
|
|
65
|
+
/** Create a new passkey credential. Owns challenge and user ID generation internally. */
|
|
66
|
+
register: (
|
|
67
|
+
options: PasskeyRegistrationOptions,
|
|
68
|
+
) => Promise<PasskeyRegistrationResult>
|
|
35
69
|
}
|
|
36
|
-
export type WebauthnStamper = Stamper
|
|
@@ -1,21 +1,42 @@
|
|
|
1
|
+
import { getWebAuthnAttestation } from '@turnkey/http'
|
|
1
2
|
import { WebauthnStamper as TurnkeyWebauthnStamper } from '@turnkey/webauthn-stamper'
|
|
2
|
-
import
|
|
3
|
+
import { base64UrlEncode, generateRandomBuffer } from '../utils/utils.js'
|
|
4
|
+
import type { PasskeyRegistrationOptions, PasskeyStamper } from './types.js'
|
|
3
5
|
|
|
4
6
|
export async function createWebauthnStamper({
|
|
5
7
|
rpId,
|
|
6
8
|
}: {
|
|
7
9
|
rpId: string
|
|
8
|
-
}): Promise<
|
|
10
|
+
}): Promise<PasskeyStamper> {
|
|
9
11
|
const inner = new TurnkeyWebauthnStamper({ rpId })
|
|
10
12
|
|
|
11
13
|
return {
|
|
12
|
-
async getPublicKey() {
|
|
13
|
-
// return await inner.();
|
|
14
|
-
return null
|
|
15
|
-
},
|
|
16
14
|
async stamp(payload: string) {
|
|
17
15
|
return await inner.stamp(payload)
|
|
18
16
|
},
|
|
19
17
|
async clear() {},
|
|
18
|
+
async register(options: PasskeyRegistrationOptions) {
|
|
19
|
+
const challenge = generateRandomBuffer()
|
|
20
|
+
const encodedChallenge = base64UrlEncode(challenge)
|
|
21
|
+
const authenticatorUserId = generateRandomBuffer()
|
|
22
|
+
|
|
23
|
+
const attestation = await getWebAuthnAttestation({
|
|
24
|
+
publicKey: {
|
|
25
|
+
rp: options.rp,
|
|
26
|
+
challenge,
|
|
27
|
+
pubKeyCredParams: [
|
|
28
|
+
{ type: 'public-key', alg: -7 },
|
|
29
|
+
{ type: 'public-key', alg: -257 },
|
|
30
|
+
],
|
|
31
|
+
user: {
|
|
32
|
+
id: authenticatorUserId,
|
|
33
|
+
name: options.userName,
|
|
34
|
+
displayName: options.userName,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return { attestation, encodedChallenge }
|
|
40
|
+
},
|
|
20
41
|
}
|
|
21
42
|
}
|
package/src/types/session.ts
CHANGED
|
@@ -3,7 +3,7 @@ export enum SessionType {
|
|
|
3
3
|
READ_WRITE = 'SESSION_TYPE_READ_WRITE',
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
export type StamperType = '
|
|
6
|
+
export type StamperType = 'apiKey' | 'passkey'
|
|
7
7
|
|
|
8
8
|
export type ZeroDevWalletSession = {
|
|
9
9
|
id: string
|
|
@@ -11,8 +11,7 @@ export type ZeroDevWalletSession = {
|
|
|
11
11
|
organizationId: string
|
|
12
12
|
stamperType: StamperType
|
|
13
13
|
sessionType?: SessionType
|
|
14
|
-
token
|
|
15
|
-
publicKey?: string
|
|
14
|
+
token: string
|
|
16
15
|
expiry: number
|
|
17
16
|
createdAt: number
|
|
18
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApiKeyStamper } from '../stampers/types.js'
|
|
2
2
|
import { derToRawSignature } from './derToRawSignature.js'
|
|
3
3
|
|
|
4
4
|
export type BuildClientSignatureParams = {
|
|
@@ -6,8 +6,8 @@ export type BuildClientSignatureParams = {
|
|
|
6
6
|
verificationToken: string
|
|
7
7
|
/** The compressed public key hex */
|
|
8
8
|
publicKey: string
|
|
9
|
-
/** The
|
|
10
|
-
stamper:
|
|
9
|
+
/** The API key stamper for signing */
|
|
10
|
+
stamper: ApiKeyStamper
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -72,7 +72,7 @@ export async function exportPrivateKey(
|
|
|
72
72
|
})
|
|
73
73
|
|
|
74
74
|
const stamperKey =
|
|
75
|
-
session.stamperType === '
|
|
75
|
+
session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
|
|
76
76
|
const stamper = wallet.client[stamperKey]
|
|
77
77
|
if (!stamper) {
|
|
78
78
|
throw new Error(`Stamper '${stamperKey}' not found on wallet.client`)
|
|
@@ -56,9 +56,7 @@ export async function exportWallet(
|
|
|
56
56
|
|
|
57
57
|
const listWalletsStamp =
|
|
58
58
|
await wallet.client[
|
|
59
|
-
session.stamperType === '
|
|
60
|
-
? 'indexedDbStamper'
|
|
61
|
-
: 'webauthnStamper'
|
|
59
|
+
session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
|
|
62
60
|
].stamp(listWalletsBody)
|
|
63
61
|
if (!listWalletsStamp) {
|
|
64
62
|
throw new Error('Failed to stamp list wallets body')
|
|
@@ -93,9 +91,7 @@ export async function exportWallet(
|
|
|
93
91
|
})
|
|
94
92
|
const exportWalletStamp =
|
|
95
93
|
await wallet.client[
|
|
96
|
-
session.stamperType === '
|
|
97
|
-
? 'indexedDbStamper'
|
|
98
|
-
: 'webauthnStamper'
|
|
94
|
+
session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
|
|
99
95
|
].stamp(exportWalletBody)
|
|
100
96
|
if (!exportWalletStamp) {
|
|
101
97
|
throw new Error('Failed to stamp export wallet body')
|
package/src/utils/hpke.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - tkhq/go-sdk/pkg/enclave_encrypt
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { gcm } from '@noble/ciphers/aes.js'
|
|
18
19
|
import { p256 } from '@noble/curves/nist.js'
|
|
19
20
|
import { expand, extract } from '@noble/hashes/hkdf.js'
|
|
20
21
|
import { sha256 } from '@noble/hashes/sha2.js'
|
|
@@ -165,42 +166,15 @@ function keySchedule(
|
|
|
165
166
|
return { key, baseNonce }
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
// noble/v2 returns) under strict TS lib settings because the underlying buffer
|
|
170
|
-
// could in principle be a SharedArrayBuffer. Copy into a fresh ArrayBuffer to
|
|
171
|
-
// satisfy the type.
|
|
172
|
-
function toArrayBuffer(u8: Uint8Array): ArrayBuffer {
|
|
173
|
-
const out = new ArrayBuffer(u8.byteLength)
|
|
174
|
-
new Uint8Array(out).set(u8)
|
|
175
|
-
return out
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function aesGcmSeal(
|
|
169
|
+
function aesGcmSeal(
|
|
179
170
|
key: Uint8Array,
|
|
180
171
|
nonce: Uint8Array,
|
|
181
172
|
aad: Uint8Array,
|
|
182
173
|
plaintext: Uint8Array,
|
|
183
|
-
):
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
'raw',
|
|
188
|
-
toArrayBuffer(key),
|
|
189
|
-
{ name: 'AES-GCM' },
|
|
190
|
-
/* extractable */ false,
|
|
191
|
-
['encrypt'],
|
|
192
|
-
)
|
|
193
|
-
const ct = await crypto.subtle.encrypt(
|
|
194
|
-
{
|
|
195
|
-
name: 'AES-GCM',
|
|
196
|
-
iv: toArrayBuffer(nonce),
|
|
197
|
-
additionalData: toArrayBuffer(aad),
|
|
198
|
-
tagLength: 128,
|
|
199
|
-
},
|
|
200
|
-
cryptoKey,
|
|
201
|
-
toArrayBuffer(plaintext),
|
|
202
|
-
)
|
|
203
|
-
return new Uint8Array(ct)
|
|
174
|
+
): Uint8Array {
|
|
175
|
+
// Returns ciphertext || tag (16 bytes appended) — matches the single-blob
|
|
176
|
+
// format Turnkey's `Sealer.Seal` and Web Crypto's AES-GCM produce.
|
|
177
|
+
return gcm(key, nonce, aad).encrypt(plaintext)
|
|
204
178
|
}
|
|
205
179
|
|
|
206
180
|
export type HpkeSealResult = {
|
|
@@ -239,7 +213,7 @@ export async function hpkeSealP256({
|
|
|
239
213
|
|
|
240
214
|
// First message of the context, sequence 0 → nonce = base_nonce.
|
|
241
215
|
const aad = concat(enc, receiverPublicKey)
|
|
242
|
-
const ciphertext =
|
|
216
|
+
const ciphertext = aesGcmSeal(key, baseNonce, aad, plaintext)
|
|
243
217
|
|
|
244
218
|
return { encappedPublic: enc, ciphertext }
|
|
245
219
|
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -18,7 +18,8 @@ export function parseSession(
|
|
|
18
18
|
throw new Error('Invalid JWT: Missing payload')
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')
|
|
22
|
+
const decoded = JSON.parse(atob(base64))
|
|
22
23
|
const {
|
|
23
24
|
exp,
|
|
24
25
|
public_key: publicKey,
|
|
@@ -68,11 +69,9 @@ export const generateRandomBuffer = (): ArrayBuffer => {
|
|
|
68
69
|
* @returns {string} - The encoded challenge.
|
|
69
70
|
*/
|
|
70
71
|
export const base64UrlEncode = (challenge: ArrayBuffer): string => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.replace(/\//g, '_')
|
|
75
|
-
.replace(/=/g, '')
|
|
72
|
+
const bytes = new Uint8Array(challenge)
|
|
73
|
+
const binary = String.fromCharCode(...bytes)
|
|
74
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
/**
|