@zerodev/wallet-core 0.0.1-alpha.18 → 0.0.1-alpha.19

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 (128) hide show
  1. package/dist/_cjs/actions/auth/getOAuthLoginUrl.js +18 -0
  2. package/dist/_cjs/actions/auth/getOAuthLoginUrl.js.map +1 -0
  3. package/dist/_cjs/actions/auth/getWhoami.js +2 -2
  4. package/dist/_cjs/actions/auth/getWhoami.js.map +1 -1
  5. package/dist/_cjs/actions/auth/index.js +3 -1
  6. package/dist/_cjs/actions/auth/index.js.map +1 -1
  7. package/dist/_cjs/actions/auth/loginWithStamp.js +5 -5
  8. package/dist/_cjs/actions/auth/loginWithStamp.js.map +1 -1
  9. package/dist/_cjs/actions/index.js +2 -1
  10. package/dist/_cjs/actions/index.js.map +1 -1
  11. package/dist/_cjs/actions/wallet/signingUtils.js +2 -2
  12. package/dist/_cjs/actions/wallet/signingUtils.js.map +1 -1
  13. package/dist/_cjs/client/createClient.js +5 -5
  14. package/dist/_cjs/client/createClient.js.map +1 -1
  15. package/dist/_cjs/client/decorators/client.js +1 -0
  16. package/dist/_cjs/client/decorators/client.js.map +1 -1
  17. package/dist/_cjs/client/transports/createTransport.js +5 -5
  18. package/dist/_cjs/client/transports/createTransport.js.map +1 -1
  19. package/dist/_cjs/client/transports/rest.js +5 -5
  20. package/dist/_cjs/client/transports/rest.js.map +1 -1
  21. package/dist/_cjs/core/createZeroDevWallet.js +29 -61
  22. package/dist/_cjs/core/createZeroDevWallet.js.map +1 -1
  23. package/dist/_cjs/index.js.map +1 -1
  24. package/dist/_cjs/stampers/indexedDbStamper.js +17 -2
  25. package/dist/_cjs/stampers/indexedDbStamper.js.map +1 -1
  26. package/dist/_cjs/stampers/webauthnStamper.js +23 -3
  27. package/dist/_cjs/stampers/webauthnStamper.js.map +1 -1
  28. package/dist/_cjs/utils/exportPrivateKey.js +1 -1
  29. package/dist/_cjs/utils/exportPrivateKey.js.map +1 -1
  30. package/dist/_cjs/utils/exportWallet.js +2 -6
  31. package/dist/_cjs/utils/exportWallet.js.map +1 -1
  32. package/dist/_cjs/utils/hpke.js +4 -15
  33. package/dist/_cjs/utils/hpke.js.map +1 -1
  34. package/dist/_cjs/utils/utils.js +5 -6
  35. package/dist/_cjs/utils/utils.js.map +1 -1
  36. package/dist/_esm/actions/auth/getOAuthLoginUrl.js +23 -0
  37. package/dist/_esm/actions/auth/getOAuthLoginUrl.js.map +1 -0
  38. package/dist/_esm/actions/auth/getWhoami.js +2 -2
  39. package/dist/_esm/actions/auth/getWhoami.js.map +1 -1
  40. package/dist/_esm/actions/auth/index.js +1 -0
  41. package/dist/_esm/actions/auth/index.js.map +1 -1
  42. package/dist/_esm/actions/auth/loginWithStamp.js +5 -5
  43. package/dist/_esm/actions/auth/loginWithStamp.js.map +1 -1
  44. package/dist/_esm/actions/index.js +1 -1
  45. package/dist/_esm/actions/index.js.map +1 -1
  46. package/dist/_esm/actions/wallet/signingUtils.js +2 -2
  47. package/dist/_esm/actions/wallet/signingUtils.js.map +1 -1
  48. package/dist/_esm/client/createClient.js +5 -5
  49. package/dist/_esm/client/createClient.js.map +1 -1
  50. package/dist/_esm/client/decorators/client.js +2 -1
  51. package/dist/_esm/client/decorators/client.js.map +1 -1
  52. package/dist/_esm/client/transports/createTransport.js +5 -5
  53. package/dist/_esm/client/transports/createTransport.js.map +1 -1
  54. package/dist/_esm/client/transports/rest.js +5 -5
  55. package/dist/_esm/client/transports/rest.js.map +1 -1
  56. package/dist/_esm/core/createZeroDevWallet.js +30 -62
  57. package/dist/_esm/core/createZeroDevWallet.js.map +1 -1
  58. package/dist/_esm/index.js.map +1 -1
  59. package/dist/_esm/stampers/indexedDbStamper.js +17 -2
  60. package/dist/_esm/stampers/indexedDbStamper.js.map +1 -1
  61. package/dist/_esm/stampers/webauthnStamper.js +23 -4
  62. package/dist/_esm/stampers/webauthnStamper.js.map +1 -1
  63. package/dist/_esm/utils/exportPrivateKey.js +1 -1
  64. package/dist/_esm/utils/exportPrivateKey.js.map +1 -1
  65. package/dist/_esm/utils/exportWallet.js +2 -6
  66. package/dist/_esm/utils/exportWallet.js.map +1 -1
  67. package/dist/_esm/utils/hpke.js +6 -22
  68. package/dist/_esm/utils/hpke.js.map +1 -1
  69. package/dist/_esm/utils/utils.js +5 -6
  70. package/dist/_esm/utils/utils.js.map +1 -1
  71. package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts +30 -0
  72. package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts.map +1 -0
  73. package/dist/_types/actions/auth/index.d.ts +1 -0
  74. package/dist/_types/actions/auth/index.d.ts.map +1 -1
  75. package/dist/_types/actions/auth/loginWithStamp.d.ts +2 -1
  76. package/dist/_types/actions/auth/loginWithStamp.d.ts.map +1 -1
  77. package/dist/_types/actions/index.d.ts +1 -1
  78. package/dist/_types/actions/index.d.ts.map +1 -1
  79. package/dist/_types/client/decorators/client.d.ts +7 -1
  80. package/dist/_types/client/decorators/client.d.ts.map +1 -1
  81. package/dist/_types/client/transports/rest.d.ts +5 -4
  82. package/dist/_types/client/transports/rest.d.ts.map +1 -1
  83. package/dist/_types/client/types.d.ts +9 -9
  84. package/dist/_types/client/types.d.ts.map +1 -1
  85. package/dist/_types/core/createZeroDevWallet.d.ts +3 -0
  86. package/dist/_types/core/createZeroDevWallet.d.ts.map +1 -1
  87. package/dist/_types/index.d.ts +1 -1
  88. package/dist/_types/index.d.ts.map +1 -1
  89. package/dist/_types/stampers/index.d.ts +1 -1
  90. package/dist/_types/stampers/index.d.ts.map +1 -1
  91. package/dist/_types/stampers/indexedDbStamper.d.ts +2 -2
  92. package/dist/_types/stampers/indexedDbStamper.d.ts.map +1 -1
  93. package/dist/_types/stampers/types.d.ts +31 -5
  94. package/dist/_types/stampers/types.d.ts.map +1 -1
  95. package/dist/_types/stampers/webauthnStamper.d.ts +2 -2
  96. package/dist/_types/stampers/webauthnStamper.d.ts.map +1 -1
  97. package/dist/_types/types/session.d.ts +2 -3
  98. package/dist/_types/types/session.d.ts.map +1 -1
  99. package/dist/_types/utils/buildClientSignature.d.ts +3 -3
  100. package/dist/_types/utils/buildClientSignature.d.ts.map +1 -1
  101. package/dist/_types/utils/exportWallet.d.ts.map +1 -1
  102. package/dist/_types/utils/hpke.d.ts.map +1 -1
  103. package/dist/_types/utils/utils.d.ts.map +1 -1
  104. package/dist/tsconfig.build.tsbuildinfo +1 -1
  105. package/package.json +2 -1
  106. package/src/actions/auth/getOAuthLoginUrl.ts +48 -0
  107. package/src/actions/auth/getWhoami.ts +2 -2
  108. package/src/actions/auth/index.ts +5 -0
  109. package/src/actions/auth/loginWithStamp.ts +7 -6
  110. package/src/actions/index.ts +3 -0
  111. package/src/actions/wallet/signingUtils.ts +2 -2
  112. package/src/client/createClient.ts +6 -6
  113. package/src/client/decorators/client.ts +13 -0
  114. package/src/client/transports/createTransport.ts +5 -5
  115. package/src/client/transports/rest.ts +11 -10
  116. package/src/client/types.ts +9 -9
  117. package/src/core/createZeroDevWallet.ts +35 -77
  118. package/src/index.ts +5 -2
  119. package/src/stampers/index.ts +2 -2
  120. package/src/stampers/indexedDbStamper.ts +24 -4
  121. package/src/stampers/types.ts +33 -5
  122. package/src/stampers/webauthnStamper.ts +27 -6
  123. package/src/types/session.ts +2 -3
  124. package/src/utils/buildClientSignature.ts +3 -3
  125. package/src/utils/exportPrivateKey.ts +1 -1
  126. package/src/utils/exportWallet.ts +2 -6
  127. package/src/utils/hpke.ts +7 -33
  128. package/src/utils/utils.ts +5 -6
@@ -1,10 +1,13 @@
1
1
  import { IndexedDbStamper as TurnkeyIndexedDbStamper } from '@turnkey/indexed-db-stamper'
2
- import type { IndexedDbStamper } from './types.js'
2
+ import { generateCompressedPublicKeyFromKeyPair } from '../utils/utils.js'
3
+ import type { ApiKeyStamper } from './types.js'
3
4
 
4
- export async function createIndexedDbStamper(): Promise<IndexedDbStamper> {
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()
@@ -15,8 +18,25 @@ export async function createIndexedDbStamper(): Promise<IndexedDbStamper> {
15
18
  async clear() {
16
19
  await inner.clear()
17
20
  },
18
- async resetKeyPair(externalKeyPair?: CryptoKeyPair) {
19
- await inner.resetKeyPair(externalKeyPair)
21
+ async resetKeyPair() {
22
+ pendingKeyPair = null
23
+ await inner.resetKeyPair()
24
+ },
25
+ async prepareKeyRotation() {
26
+ const keyPair = await crypto.subtle.generateKey(
27
+ { name: 'ECDSA', namedCurve: 'P-256' },
28
+ false,
29
+ ['sign', 'verify'],
30
+ )
31
+ pendingKeyPair = keyPair
32
+ return await generateCompressedPublicKeyFromKeyPair(keyPair)
33
+ },
34
+ async commitKeyRotation() {
35
+ if (!pendingKeyPair) {
36
+ throw new Error('No pending key rotation to commit')
37
+ }
38
+ await inner.resetKeyPair(pendingKeyPair)
39
+ pendingKeyPair = null
20
40
  },
21
41
  }
22
42
  }
@@ -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,35 @@ export type IframeStamper = Stamper & {
30
30
  applySettings(settings: { styles?: Record<string, string> }): Promise<boolean>
31
31
  }
32
32
 
33
- export type IndexedDbStamper = Stamper & {
34
- resetKeyPair: (externalKeyPair?: CryptoKeyPair) => Promise<void>
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
+ export type Attestation = {
44
+ attestationObject: string
45
+ clientDataJson: string
46
+ credentialId: string
47
+ }
48
+
49
+ export type PasskeyRegistrationOptions = {
50
+ rp: { id: string; name: string }
51
+ userName: string
52
+ }
53
+
54
+ export type PasskeyRegistrationResult = {
55
+ attestation: Attestation
56
+ encodedChallenge: string
57
+ }
58
+
59
+ export type PasskeyStamper = Stamper & {
60
+ /** Create a new passkey credential. Owns challenge and user ID generation internally. */
61
+ register: (
62
+ options: PasskeyRegistrationOptions,
63
+ ) => Promise<PasskeyRegistrationResult>
35
64
  }
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 type { WebauthnStamper } from './types.js'
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<WebauthnStamper> {
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
  }
@@ -3,7 +3,7 @@ export enum SessionType {
3
3
  READ_WRITE = 'SESSION_TYPE_READ_WRITE',
4
4
  }
5
5
 
6
- export type StamperType = 'iframe' | 'indexedDb' | 'passkey'
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?: string
15
- publicKey?: string
14
+ token: string
16
15
  expiry: number
17
16
  createdAt: number
18
17
  }
@@ -1,4 +1,4 @@
1
- import type { IndexedDbStamper } from '../stampers/types.js'
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 IndexedDB stamper for signing */
10
- stamper: IndexedDbStamper
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 === 'indexedDb' ? 'indexedDbStamper' : 'webauthnStamper'
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 === 'indexedDb'
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 === 'indexedDb'
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
- // Web Crypto's BufferSource type rejects `Uint8Array<ArrayBufferLike>` (which
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
- ): Promise<Uint8Array> {
184
- // Web Crypto returns ciphertext || tag (16 bytes appended). Matches the
185
- // single-blob format Turnkey's `Sealer.Seal` produces.
186
- const cryptoKey = await crypto.subtle.importKey(
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 = await aesGcmSeal(key, baseNonce, aad, plaintext)
216
+ const ciphertext = aesGcmSeal(key, baseNonce, aad, plaintext)
243
217
 
244
218
  return { encappedPublic: enc, ciphertext }
245
219
  }
@@ -18,7 +18,8 @@ export function parseSession(
18
18
  throw new Error('Invalid JWT: Missing payload')
19
19
  }
20
20
 
21
- const decoded = JSON.parse(Buffer.from(payload, 'base64').toString())
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
- return Buffer.from(challenge)
72
- .toString('base64')
73
- .replace(/\+/g, '-')
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
  /**