@zerodev/wallet-react 0.0.1-alpha.13 → 0.0.1-alpha.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerodev/wallet-react",
3
- "version": "0.0.1-alpha.13",
3
+ "version": "0.0.1-alpha.15",
4
4
  "description": "React hooks for ZeroDev Wallet SDK",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/_cjs/index.js",
@@ -30,7 +30,7 @@
30
30
  "wagmi": "^3.0.0",
31
31
  "zustand": "^5.0.3",
32
32
  "ox": "^0.3.0",
33
- "@zerodev/wallet-core": "0.0.1-alpha.12"
33
+ "@zerodev/wallet-core": "0.0.1-alpha.14"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/react": "^19",
@@ -569,7 +569,7 @@ describe('React Actions', () => {
569
569
  })
570
570
  })
571
571
 
572
- it('completes full OAuth success flow', async () => {
572
+ it('completes full OAuth success flow with sessionId', async () => {
573
573
  const wallet = createMockWallet()
574
574
  wallet.getSession.mockResolvedValue({ id: 'oauth-session' })
575
575
  wallet.toAccount.mockResolvedValue({ address: '0xoauth' })
@@ -578,11 +578,14 @@ describe('React Actions', () => {
578
578
  const config = createMockConfig(connector)
579
579
 
580
580
  mockOpenOAuthPopup.mockReturnValue({ closed: false } as Window)
581
- // Simulate the success callback being called immediately
581
+ // Simulate the success callback being called with sessionId
582
582
  mockListenForOAuthMessage.mockImplementation(
583
- (_win: Window, _origin: string, onSuccess: () => void) => {
584
- // Call onSuccess asynchronously to simulate real behavior
585
- setTimeout(() => onSuccess(), 0)
583
+ (
584
+ _win: Window,
585
+ _origin: string,
586
+ onSuccess: (sessionId: string) => void,
587
+ ) => {
588
+ setTimeout(() => onSuccess('test-session-id'), 0)
586
589
  return () => {}
587
590
  },
588
591
  )
@@ -592,6 +595,7 @@ describe('React Actions', () => {
592
595
  expect(wallet.auth).toHaveBeenCalledWith({
593
596
  type: 'oauth',
594
597
  provider: 'google',
598
+ sessionId: 'test-session-id',
595
599
  })
596
600
  expect(store.getState().setEoaAccount).toHaveBeenCalledWith({
597
601
  address: '0xoauth',
@@ -611,8 +615,12 @@ describe('React Actions', () => {
611
615
 
612
616
  mockOpenOAuthPopup.mockReturnValue({ closed: false } as Window)
613
617
  mockListenForOAuthMessage.mockImplementation(
614
- (_win: Window, _origin: string, onSuccess: () => void) => {
615
- setTimeout(() => onSuccess(), 0)
618
+ (
619
+ _win: Window,
620
+ _origin: string,
621
+ onSuccess: (sessionId: string) => void,
622
+ ) => {
623
+ setTimeout(() => onSuccess('session-id'), 0)
616
624
  return () => {}
617
625
  },
618
626
  )
@@ -633,7 +641,7 @@ describe('React Actions', () => {
633
641
  (
634
642
  _win: Window,
635
643
  _origin: string,
636
- _onSuccess: () => void,
644
+ _onSuccess: (sessionId: string) => void,
637
645
  onError: (error: Error) => void,
638
646
  ) => {
639
647
  setTimeout(() => onError(new Error('Window closed')), 0)
package/src/actions.ts CHANGED
@@ -158,13 +158,13 @@ export async function authenticateOAuth(
158
158
  const cleanup = listenForOAuthMessage(
159
159
  authWindow,
160
160
  window.location.origin,
161
- async () => {
161
+ async (sessionId) => {
162
162
  try {
163
163
  // Complete OAuth authentication with wallet-core
164
- // The backend has stored the OAuth session in a cookie
165
164
  await wallet.auth({
166
165
  type: 'oauth',
167
166
  provider: parameters.provider,
167
+ sessionId,
168
168
  })
169
169
 
170
170
  const [session, eoaAccount] = await Promise.all([
@@ -208,6 +208,7 @@ export async function sendOTP(
208
208
  parameters: {
209
209
  email: string
210
210
  emailCustomization?: { magicLinkTemplate?: string }
211
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
211
212
  connector?: Connector
212
213
  },
213
214
  ): Promise<{ otpId: string }> {
@@ -227,6 +228,9 @@ export async function sendOTP(
227
228
  ...(parameters.emailCustomization && {
228
229
  emailCustomization: parameters.emailCustomization,
229
230
  }),
231
+ ...(parameters.otpCodeCustomization && {
232
+ otpCodeCustomization: parameters.otpCodeCustomization,
233
+ }),
230
234
  })
231
235
 
232
236
  return {
@@ -238,6 +242,7 @@ export declare namespace sendOTP {
238
242
  type Parameters = {
239
243
  email: string
240
244
  emailCustomization?: { magicLinkTemplate?: string }
245
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
241
246
  connector?: Connector
242
247
  }
243
248
  type ReturnType = { otpId: string }
@@ -497,6 +502,7 @@ export async function sendMagicLink(
497
502
  parameters: {
498
503
  email: string
499
504
  redirectURL: string
505
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
500
506
  connector?: Connector
501
507
  },
502
508
  ): Promise<{ otpId: string }> {
@@ -513,6 +519,9 @@ export async function sendMagicLink(
513
519
  mode: 'send',
514
520
  email: parameters.email,
515
521
  redirectURL: parameters.redirectURL,
522
+ ...(parameters.otpCodeCustomization && {
523
+ otpCodeCustomization: parameters.otpCodeCustomization,
524
+ }),
516
525
  })
517
526
 
518
527
  return {
@@ -524,6 +533,7 @@ export declare namespace sendMagicLink {
524
533
  type Parameters = {
525
534
  email: string
526
535
  redirectURL: string
536
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
527
537
  connector?: Connector
528
538
  }
529
539
  type ReturnType = { otpId: string }
package/src/connector.ts CHANGED
@@ -16,6 +16,7 @@ import { getAAUrl } from './utils/aaUtils.js'
16
16
  // OAuth URL parameter used to detect callback
17
17
  const OAUTH_SUCCESS_PARAM = 'oauth_success'
18
18
  const OAUTH_PROVIDER_PARAM = 'oauth_provider'
19
+ const OAUTH_SESSION_ID_PARAM = 'session_id'
19
20
 
20
21
  /**
21
22
  * Detect OAuth callback from URL params and handle it.
@@ -43,9 +44,10 @@ async function detectAndHandleOAuthCallback(
43
44
  console.log('OAuth callback detected, completing authentication...')
44
45
  const provider = (params.get(OAUTH_PROVIDER_PARAM) ||
45
46
  'google') as OAuthProvider
47
+ const sessionId = params.get(OAUTH_SESSION_ID_PARAM) || ''
46
48
 
47
49
  try {
48
- await wallet.auth({ type: 'oauth', provider })
50
+ await wallet.auth({ type: 'oauth', provider, sessionId })
49
51
 
50
52
  const [session, eoaAccount] = await Promise.all([
51
53
  wallet.getSession(),
@@ -58,6 +60,7 @@ async function detectAndHandleOAuthCallback(
58
60
  // Clean up URL params
59
61
  params.delete(OAUTH_SUCCESS_PARAM)
60
62
  params.delete(OAUTH_PROVIDER_PARAM)
63
+ params.delete(OAUTH_SESSION_ID_PARAM)
61
64
  const newUrl = params.toString()
62
65
  ? `${window.location.pathname}?${params.toString()}`
63
66
  : window.location.pathname
package/src/oauth.test.ts CHANGED
@@ -188,15 +188,35 @@ describe('OAuth utilities', () => {
188
188
  vi.useRealTimers()
189
189
  })
190
190
 
191
- it('calls onSuccess when oauth_success message received', () => {
191
+ it('calls onSuccess with sessionId when oauth_success message received', () => {
192
192
  const cleanup = listenForOAuthMessage(
193
193
  mockWindow as Window,
194
194
  'https://app.example.com',
195
- onSuccessMock as () => void,
195
+ onSuccessMock as (sessionId: string) => void,
196
+ onErrorMock as (error: Error) => void,
197
+ )
198
+
199
+ // Simulate receiving a message with sessionId
200
+ const event = new MessageEvent('message', {
201
+ data: { type: 'oauth_success', sessionId: 'test-session-123' },
202
+ origin: 'https://app.example.com',
203
+ })
204
+ window.dispatchEvent(event)
205
+
206
+ expect(onSuccessMock).toHaveBeenCalledOnce()
207
+ expect(onSuccessMock).toHaveBeenCalledWith('test-session-123')
208
+ expect(onErrorMock).not.toHaveBeenCalled()
209
+ cleanup()
210
+ })
211
+
212
+ it('calls onSuccess with empty string when sessionId is missing', () => {
213
+ const cleanup = listenForOAuthMessage(
214
+ mockWindow as Window,
215
+ 'https://app.example.com',
216
+ onSuccessMock as (sessionId: string) => void,
196
217
  onErrorMock as (error: Error) => void,
197
218
  )
198
219
 
199
- // Simulate receiving a message
200
220
  const event = new MessageEvent('message', {
201
221
  data: { type: 'oauth_success' },
202
222
  origin: 'https://app.example.com',
@@ -204,6 +224,7 @@ describe('OAuth utilities', () => {
204
224
  window.dispatchEvent(event)
205
225
 
206
226
  expect(onSuccessMock).toHaveBeenCalledOnce()
227
+ expect(onSuccessMock).toHaveBeenCalledWith('')
207
228
  expect(onErrorMock).not.toHaveBeenCalled()
208
229
  cleanup()
209
230
  })
@@ -212,7 +233,7 @@ describe('OAuth utilities', () => {
212
233
  const cleanup = listenForOAuthMessage(
213
234
  mockWindow as Window,
214
235
  'https://app.example.com',
215
- onSuccessMock as () => void,
236
+ onSuccessMock as (sessionId: string) => void,
216
237
  onErrorMock as (error: Error) => void,
217
238
  )
218
239
 
@@ -232,7 +253,7 @@ describe('OAuth utilities', () => {
232
253
  const cleanup = listenForOAuthMessage(
233
254
  mockWindow as Window,
234
255
  'https://app.example.com',
235
- onSuccessMock as () => void,
256
+ onSuccessMock as (sessionId: string) => void,
236
257
  onErrorMock as (error: Error) => void,
237
258
  )
238
259
 
@@ -251,7 +272,7 @@ describe('OAuth utilities', () => {
251
272
  const cleanup = listenForOAuthMessage(
252
273
  mockWindow as Window,
253
274
  'https://app.example.com',
254
- onSuccessMock as () => void,
275
+ onSuccessMock as (sessionId: string) => void,
255
276
  onErrorMock as (error: Error) => void,
256
277
  )
257
278
 
@@ -268,7 +289,7 @@ describe('OAuth utilities', () => {
268
289
  const cleanup = listenForOAuthMessage(
269
290
  mockWindow as Window,
270
291
  'https://app.example.com',
271
- onSuccessMock as () => void,
292
+ onSuccessMock as (sessionId: string) => void,
272
293
  onErrorMock as (error: Error) => void,
273
294
  )
274
295
 
@@ -288,7 +309,7 @@ describe('OAuth utilities', () => {
288
309
  const cleanup = listenForOAuthMessage(
289
310
  mockWindow as Window,
290
311
  'https://app.example.com',
291
- onSuccessMock as () => void,
312
+ onSuccessMock as (sessionId: string) => void,
292
313
  onErrorMock as (error: Error) => void,
293
314
  )
294
315
 
@@ -323,7 +344,7 @@ describe('OAuth utilities', () => {
323
344
  const cleanup = listenForOAuthMessage(
324
345
  mockWindow as Window,
325
346
  'https://app.example.com',
326
- onSuccessMock as () => void,
347
+ onSuccessMock as (sessionId: string) => void,
327
348
  onErrorMock as (error: Error) => void,
328
349
  )
329
350
 
@@ -345,7 +366,7 @@ describe('OAuth utilities', () => {
345
366
  const cleanup = listenForOAuthMessage(
346
367
  mockWindow as Window,
347
368
  'https://app.example.com',
348
- onSuccessMock as () => void,
369
+ onSuccessMock as (sessionId: string) => void,
349
370
  onErrorMock as (error: Error) => void,
350
371
  )
351
372
 
@@ -391,7 +412,20 @@ describe('OAuth utilities', () => {
391
412
  window.close = originalClose
392
413
  })
393
414
 
394
- it('returns true and posts success message on oauth_success=true', () => {
415
+ it('returns true and posts success message with sessionId on oauth_success=true', () => {
416
+ window.location.search = '?oauth_success=true&session_id=abc123'
417
+
418
+ const result = handleOAuthCallback()
419
+
420
+ expect(result).toBe(true)
421
+ expect(window.opener?.postMessage).toHaveBeenCalledWith(
422
+ { type: 'oauth_success', sessionId: 'abc123' },
423
+ 'https://app.example.com',
424
+ )
425
+ expect(window.close).toHaveBeenCalled()
426
+ })
427
+
428
+ it('posts success message without sessionId when session_id param is missing', () => {
395
429
  window.location.search = '?oauth_success=true'
396
430
 
397
431
  const result = handleOAuthCallback()
package/src/oauth.ts CHANGED
@@ -62,6 +62,7 @@ export function buildBackendOAuthUrl(params: BackendOAuthFlowParams): string {
62
62
 
63
63
  export type OAuthMessageData = {
64
64
  type: 'oauth_success' | 'oauth_error'
65
+ sessionId?: string
65
66
  error?: string
66
67
  }
67
68
 
@@ -72,7 +73,7 @@ export type OAuthMessageData = {
72
73
  export function listenForOAuthMessage(
73
74
  authWindow: Window,
74
75
  expectedOrigin: string,
75
- onSuccess: () => void,
76
+ onSuccess: (sessionId: string) => void,
76
77
  onError: (error: Error) => void,
77
78
  ): () => void {
78
79
  let cleaned = false
@@ -84,7 +85,7 @@ export function listenForOAuthMessage(
84
85
 
85
86
  if (event.data.type === 'oauth_success') {
86
87
  cleanup()
87
- onSuccess()
88
+ onSuccess(event.data.sessionId || '')
88
89
  } else if (event.data.type === 'oauth_error') {
89
90
  cleanup()
90
91
  onError(new Error(event.data.error || 'OAuth authentication failed'))
@@ -119,13 +120,13 @@ export function handleOAuthCallback(successParam = 'oauth_success'): boolean {
119
120
  const urlParams = new URLSearchParams(window.location.search)
120
121
  const isSuccess = urlParams.get(successParam) === 'true'
121
122
  const error = urlParams.get('error')
123
+ const sessionId = urlParams.get('session_id') ?? undefined
122
124
 
123
125
  if (window.opener) {
124
126
  if (isSuccess) {
125
- window.opener.postMessage(
126
- { type: 'oauth_success' } satisfies OAuthMessageData,
127
- window.location.origin,
128
- )
127
+ const message: OAuthMessageData = { type: 'oauth_success' }
128
+ if (sessionId) message.sessionId = sessionId
129
+ window.opener.postMessage(message, window.location.origin)
129
130
  window.close()
130
131
  return true
131
132
  }