@zerodev/wallet-react 0.0.1-alpha.6 → 0.0.1-alpha.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @zerodev/wallet-react
2
2
 
3
+ ## 0.0.1-alpha.8
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: use internal state for get user email
8
+
9
+ ## 0.0.1-alpha.7
10
+
11
+ ### Patch Changes
12
+
13
+ - Add OTP authentication via Turnkey Auth Proxy
14
+ - Add OAuth backend PKCE flow
15
+ - Add getUserEmail method and React hook
16
+ - Fix passkey login endpoint
17
+ - Fix signature verification with JSON canonicalization
18
+ - Rename turnkeySession to session in OAuth response
19
+ - Updated dependencies
20
+ - @zerodev/wallet-core@0.0.1-alpha.7
21
+
3
22
  ## 0.0.1-alpha.6
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -36,12 +36,7 @@ const config = createConfig({
36
36
  connectors: [
37
37
  zeroDevWallet({
38
38
  projectId: 'YOUR_PROJECT_ID',
39
- aaUrl: 'YOUR_AA_PROVIDER_URL',
40
39
  chains: [sepolia],
41
- oauthConfig: {
42
- googleClientId: 'YOUR_GOOGLE_CLIENT_ID',
43
- redirectUri: 'http://localhost:3000',
44
- },
45
40
  })
46
41
  ],
47
42
  transports: {
@@ -150,7 +145,8 @@ await loginPasskey.mutateAsync({ email: 'user@example.com' })
150
145
  ```typescript
151
146
  const authenticateOAuth = useAuthenticateOAuth()
152
147
 
153
- // Opens popup automatically, handles token exchange
148
+ // Opens popup, backend handles PKCE and token exchange
149
+ // No callback page or OAuth library needed - SDK handles everything
154
150
  await authenticateOAuth.mutateAsync({
155
151
  provider: OAUTH_PROVIDERS.GOOGLE
156
152
  })
@@ -163,7 +159,7 @@ const sendOTP = useSendOTP()
163
159
  const verifyOTP = useVerifyOTP()
164
160
 
165
161
  // Send OTP code
166
- const { otpId, subOrganizationId } = await sendOTP.mutateAsync({
162
+ const { otpId } = await sendOTP.mutateAsync({
167
163
  email: 'user@example.com'
168
164
  })
169
165
 
@@ -171,7 +167,6 @@ const { otpId, subOrganizationId } = await sendOTP.mutateAsync({
171
167
  await verifyOTP.mutateAsync({
172
168
  code: '123456',
173
169
  otpId,
174
- subOrganizationId
175
170
  })
176
171
  ```
177
172
 
@@ -182,18 +177,11 @@ type ZeroDevWalletConnectorParams = {
182
177
  projectId: string // Required: Your ZeroDev project ID
183
178
  organizationId?: string // Optional: Turnkey organization ID
184
179
  proxyBaseUrl?: string // Optional: KMS proxy URL
185
- aaUrl: string // Required: Bundler/paymaster URL
186
180
  chains: readonly Chain[] // Required: Supported chains
187
181
  rpId?: string // Optional: WebAuthn RP ID
188
182
  sessionStorage?: StorageAdapter // Optional: Custom session storage
189
183
  autoRefreshSession?: boolean // Optional: Auto-refresh (default: true)
190
184
  sessionWarningThreshold?: number // Optional: Refresh threshold in ms (default: 60000)
191
- oauthConfig?: OAuthConfig // Optional: OAuth configuration
192
- }
193
-
194
- type OAuthConfig = {
195
- googleClientId?: string
196
- redirectUri: string
197
185
  }
198
186
  ```
199
187
 
@@ -246,6 +234,18 @@ await exportPrivateKey.mutateAsync({
246
234
  })
247
235
  ```
248
236
 
237
+ ### Get User Email
238
+
239
+ ```typescript
240
+ const getUserEmail = useGetUserEmail()
241
+
242
+ // Fetch user's email from the backend
243
+ const { email } = await getUserEmail.mutateAsync({
244
+ organizationId: session.organizationId,
245
+ projectId: 'your-project-id'
246
+ })
247
+ ```
248
+
249
249
  ## API Reference
250
250
 
251
251
  ### Hooks
@@ -260,6 +260,7 @@ All hooks follow the TanStack Query mutation pattern:
260
260
  - `useRefreshSession()` - Manually refresh session
261
261
  - `useExportWallet()` - Export wallet seed phrase
262
262
  - `useExportPrivateKey()` - Export wallet private key
263
+ - `useGetUserEmail()` - Get user's email address
263
264
 
264
265
  ### Connector
265
266
 
@@ -272,7 +273,6 @@ All hooks follow the TanStack Query mutation pattern:
272
273
  ### Types
273
274
 
274
275
  - `OAuthProvider` - OAuth provider type
275
- - `OAuthConfig` - OAuth configuration type
276
276
  - `ZeroDevWalletConnectorParams` - Connector parameters
277
277
  - `ZeroDevWalletState` - Store state type
278
278
  - `ZeroDevProvider` - EIP-1193 provider type
@@ -6,6 +6,7 @@ exports.authenticateOAuth = authenticateOAuth;
6
6
  exports.sendOTP = sendOTP;
7
7
  exports.verifyOTP = verifyOTP;
8
8
  exports.refreshSession = refreshSession;
9
+ exports.getUserEmail = getUserEmail;
9
10
  exports.exportWallet = exportWallet;
10
11
  exports.exportPrivateKey = exportPrivateKey;
11
12
  const actions_1 = require("@wagmi/core/actions");
@@ -64,40 +65,29 @@ async function authenticateOAuth(config, parameters) {
64
65
  if (!wallet)
65
66
  throw new Error('Wallet not initialized');
66
67
  if (!oauthConfig) {
67
- throw new Error('OAuth is not configured. Please provide oauthConfig to zeroDevWallet connector.');
68
- }
69
- let clientId = parameters.clientId;
70
- if (!clientId) {
71
- clientId = oauthConfig.googleClientId;
72
- }
73
- if (!clientId) {
74
- throw new Error(`Client ID not configured for ${parameters.provider}`);
75
- }
76
- if (!oauthConfig.redirectUri) {
77
- throw new Error('OAuth redirect URI is not configured.');
68
+ throw new Error('Wallet not initialized. Please wait for connector setup.');
78
69
  }
79
70
  const publicKey = await wallet.getPublicKey();
80
71
  if (!publicKey) {
81
72
  throw new Error('Failed to get wallet public key');
82
73
  }
83
- const nonce = (0, oauth_js_1.generateOAuthNonce)(publicKey);
84
- const oauthUrl = (0, oauth_js_1.buildOAuthUrl)({
74
+ const oauthUrl = (0, oauth_js_1.buildBackendOAuthUrl)({
85
75
  provider: parameters.provider,
86
- clientId,
87
- redirectUri: oauthConfig.redirectUri,
88
- nonce,
76
+ backendUrl: oauthConfig.backendUrl,
77
+ projectId: oauthConfig.projectId,
78
+ publicKey,
79
+ returnTo: `${window.location.origin}?oauth_success=true&oauth_provider=${parameters.provider}`,
89
80
  });
90
81
  const authWindow = (0, oauth_js_1.openOAuthPopup)(oauthUrl);
91
82
  if (!authWindow) {
92
83
  throw new Error(`Failed to open ${parameters.provider} login window.`);
93
84
  }
94
85
  return new Promise((resolve, reject) => {
95
- (0, oauth_js_1.pollOAuthPopup)(authWindow, window.location.origin, async (idToken) => {
86
+ const cleanup = (0, oauth_js_1.listenForOAuthMessage)(authWindow, window.location.origin, async () => {
96
87
  try {
97
88
  await wallet.auth({
98
89
  type: 'oauth',
99
90
  provider: parameters.provider,
100
- credential: idToken,
101
91
  });
102
92
  const [session, eoaAccount] = await Promise.all([
103
93
  wallet.getSession(),
@@ -111,7 +101,10 @@ async function authenticateOAuth(config, parameters) {
111
101
  catch (err) {
112
102
  reject(err);
113
103
  }
114
- }, reject);
104
+ }, (error) => {
105
+ cleanup();
106
+ reject(error);
107
+ });
115
108
  });
116
109
  }
117
110
  async function sendOTP(config, parameters) {
@@ -131,7 +124,6 @@ async function sendOTP(config, parameters) {
131
124
  });
132
125
  return {
133
126
  otpId: result.otpId,
134
- subOrganizationId: result.subOrganizationId,
135
127
  };
136
128
  }
137
129
  async function verifyOTP(config, parameters) {
@@ -145,7 +137,6 @@ async function verifyOTP(config, parameters) {
145
137
  mode: 'verifyOtp',
146
138
  otpId: parameters.otpId,
147
139
  otpCode: parameters.code,
148
- subOrganizationId: parameters.subOrganizationId,
149
140
  });
150
141
  const [session, eoaAccount] = await Promise.all([
151
142
  wallet.getSession(),
@@ -167,6 +158,25 @@ async function refreshSession(config, parameters = {}) {
167
158
  store.getState().setSession(newSession || null);
168
159
  return newSession;
169
160
  }
161
+ async function getUserEmail(config) {
162
+ const connector = getZeroDevConnector(config);
163
+ const store = await connector.getStore();
164
+ const wallet = store.getState().wallet;
165
+ if (!wallet)
166
+ throw new Error('Wallet not initialized');
167
+ const oauthConfig = store.getState().oauthConfig;
168
+ if (!oauthConfig) {
169
+ throw new Error('Wallet not initialized. Please wait for connector setup.');
170
+ }
171
+ const session = store.getState().session;
172
+ if (!session) {
173
+ throw new Error('No active session');
174
+ }
175
+ return await wallet.client.getUserEmail({
176
+ organizationId: session.organizationId,
177
+ projectId: oauthConfig.projectId,
178
+ });
179
+ }
170
180
  async function exportWallet(config, parameters) {
171
181
  const connector = parameters.connector ?? getZeroDevConnector(config);
172
182
  const store = await connector.getStore();
@@ -6,9 +6,48 @@ const sdk_1 = require("@zerodev/sdk");
6
6
  const constants_1 = require("@zerodev/sdk/constants");
7
7
  const wallet_core_1 = require("@zerodev/wallet-core");
8
8
  const viem_1 = require("viem");
9
+ const oauth_js_1 = require("./oauth.js");
9
10
  const provider_js_1 = require("./provider.js");
10
11
  const store_js_1 = require("./store.js");
11
12
  const aaUtils_js_1 = require("./utils/aaUtils.js");
13
+ const OAUTH_SUCCESS_PARAM = 'oauth_success';
14
+ const OAUTH_PROVIDER_PARAM = 'oauth_provider';
15
+ async function detectAndHandleOAuthCallback(wallet, store) {
16
+ if (typeof window === 'undefined')
17
+ return false;
18
+ const params = new URLSearchParams(window.location.search);
19
+ const isOAuthCallback = params.get(OAUTH_SUCCESS_PARAM) === 'true';
20
+ if (!isOAuthCallback)
21
+ return false;
22
+ if (window.opener) {
23
+ (0, oauth_js_1.handleOAuthCallback)(OAUTH_SUCCESS_PARAM);
24
+ return true;
25
+ }
26
+ console.log('OAuth callback detected, completing authentication...');
27
+ const provider = (params.get(OAUTH_PROVIDER_PARAM) ||
28
+ 'google');
29
+ try {
30
+ await wallet.auth({ type: 'oauth', provider });
31
+ const [session, eoaAccount] = await Promise.all([
32
+ wallet.getSession(),
33
+ wallet.toAccount(),
34
+ ]);
35
+ store.getState().setEoaAccount(eoaAccount);
36
+ store.getState().setSession(session || null);
37
+ params.delete(OAUTH_SUCCESS_PARAM);
38
+ params.delete(OAUTH_PROVIDER_PARAM);
39
+ const newUrl = params.toString()
40
+ ? `${window.location.pathname}?${params.toString()}`
41
+ : window.location.pathname;
42
+ window.history.replaceState({}, '', newUrl);
43
+ console.log('OAuth authentication completed');
44
+ return true;
45
+ }
46
+ catch (error) {
47
+ console.error('OAuth authentication failed:', error);
48
+ return false;
49
+ }
50
+ }
12
51
  function zeroDevWallet(params) {
13
52
  return (0, core_1.createConnector)((wagmiConfig) => {
14
53
  let store;
@@ -29,9 +68,10 @@ function zeroDevWallet(params) {
29
68
  });
30
69
  store = (0, store_js_1.createZeroDevWalletStore)();
31
70
  store.getState().setWallet(wallet);
32
- if (params.oauthConfig) {
33
- store.getState().setOAuthConfig(params.oauthConfig);
34
- }
71
+ store.getState().setOAuthConfig({
72
+ backendUrl: params.proxyBaseUrl || `${wallet_core_1.KMS_SERVER_URL}/api/v1`,
73
+ projectId: params.projectId,
74
+ });
35
75
  provider = (0, provider_js_1.createProvider)({
36
76
  store,
37
77
  config: params,
@@ -44,6 +84,7 @@ function zeroDevWallet(params) {
44
84
  store.getState().setEoaAccount(eoaAccount);
45
85
  store.getState().setSession(session);
46
86
  }
87
+ await detectAndHandleOAuthCallback(wallet, store);
47
88
  console.log('ZeroDevWallet connector initialized');
48
89
  };
49
90
  return {
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useGetUserEmail = useGetUserEmail;
5
+ const react_query_1 = require("@tanstack/react-query");
6
+ const wagmi_1 = require("wagmi");
7
+ const actions_js_1 = require("../actions.js");
8
+ function useGetUserEmail(parameters) {
9
+ const { query } = parameters;
10
+ const config = (0, wagmi_1.useConfig)(parameters);
11
+ return (0, react_query_1.useQuery)({
12
+ ...query,
13
+ queryKey: ['getUserEmail'],
14
+ queryFn: async () => {
15
+ return (0, actions_js_1.getUserEmail)(config);
16
+ },
17
+ enabled: Boolean(config),
18
+ });
19
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createZeroDevWalletStore = exports.OAUTH_PROVIDERS = exports.useVerifyOTP = exports.useSendOTP = exports.useRegisterPasskey = exports.useRefreshSession = exports.useLoginPasskey = exports.useExportWallet = exports.useExportPrivateKey = exports.useAuthenticateOAuth = exports.zeroDevWallet = void 0;
3
+ exports.createZeroDevWalletStore = exports.OAUTH_PROVIDERS = exports.listenForOAuthMessage = exports.handleOAuthCallback = exports.buildBackendOAuthUrl = exports.useVerifyOTP = exports.useSendOTP = exports.useRegisterPasskey = exports.useRefreshSession = exports.useLoginPasskey = exports.useGetUserEmail = exports.useExportWallet = exports.useExportPrivateKey = exports.useAuthenticateOAuth = exports.zeroDevWallet = void 0;
4
4
  var connector_js_1 = require("./connector.js");
5
5
  Object.defineProperty(exports, "zeroDevWallet", { enumerable: true, get: function () { return connector_js_1.zeroDevWallet; } });
6
6
  var useAuthenticateOAuth_js_1 = require("./hooks/useAuthenticateOAuth.js");
@@ -9,6 +9,8 @@ var useExportPrivateKey_js_1 = require("./hooks/useExportPrivateKey.js");
9
9
  Object.defineProperty(exports, "useExportPrivateKey", { enumerable: true, get: function () { return useExportPrivateKey_js_1.useExportPrivateKey; } });
10
10
  var useExportWallet_js_1 = require("./hooks/useExportWallet.js");
11
11
  Object.defineProperty(exports, "useExportWallet", { enumerable: true, get: function () { return useExportWallet_js_1.useExportWallet; } });
12
+ var useGetUserEmail_js_1 = require("./hooks/useGetUserEmail.js");
13
+ Object.defineProperty(exports, "useGetUserEmail", { enumerable: true, get: function () { return useGetUserEmail_js_1.useGetUserEmail; } });
12
14
  var useLoginPasskey_js_1 = require("./hooks/useLoginPasskey.js");
13
15
  Object.defineProperty(exports, "useLoginPasskey", { enumerable: true, get: function () { return useLoginPasskey_js_1.useLoginPasskey; } });
14
16
  var useRefreshSession_js_1 = require("./hooks/useRefreshSession.js");
@@ -20,6 +22,9 @@ Object.defineProperty(exports, "useSendOTP", { enumerable: true, get: function (
20
22
  var useVerifyOTP_js_1 = require("./hooks/useVerifyOTP.js");
21
23
  Object.defineProperty(exports, "useVerifyOTP", { enumerable: true, get: function () { return useVerifyOTP_js_1.useVerifyOTP; } });
22
24
  var oauth_js_1 = require("./oauth.js");
25
+ Object.defineProperty(exports, "buildBackendOAuthUrl", { enumerable: true, get: function () { return oauth_js_1.buildBackendOAuthUrl; } });
26
+ Object.defineProperty(exports, "handleOAuthCallback", { enumerable: true, get: function () { return oauth_js_1.handleOAuthCallback; } });
27
+ Object.defineProperty(exports, "listenForOAuthMessage", { enumerable: true, get: function () { return oauth_js_1.listenForOAuthMessage; } });
23
28
  Object.defineProperty(exports, "OAUTH_PROVIDERS", { enumerable: true, get: function () { return oauth_js_1.OAUTH_PROVIDERS; } });
24
29
  var store_js_1 = require("./store.js");
25
30
  Object.defineProperty(exports, "createZeroDevWalletStore", { enumerable: true, get: function () { return store_js_1.createZeroDevWalletStore; } });
@@ -1,42 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OAUTH_PROVIDERS = void 0;
4
- exports.buildOAuthUrl = buildOAuthUrl;
5
4
  exports.openOAuthPopup = openOAuthPopup;
6
- exports.extractOAuthToken = extractOAuthToken;
7
- exports.pollOAuthPopup = pollOAuthPopup;
8
5
  exports.generateOAuthNonce = generateOAuthNonce;
6
+ exports.buildBackendOAuthUrl = buildBackendOAuthUrl;
7
+ exports.listenForOAuthMessage = listenForOAuthMessage;
8
+ exports.handleOAuthCallback = handleOAuthCallback;
9
9
  const viem_1 = require("viem");
10
10
  exports.OAUTH_PROVIDERS = {
11
11
  GOOGLE: 'google',
12
12
  };
13
- const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
14
13
  const POPUP_WIDTH = 500;
15
14
  const POPUP_HEIGHT = 600;
16
- function buildOAuthUrl(params) {
17
- const { provider, clientId, redirectUri, nonce, state } = params;
18
- if (provider !== exports.OAUTH_PROVIDERS.GOOGLE) {
19
- throw new Error(`Unsupported OAuth provider: ${provider}`);
20
- }
21
- const authUrl = new URL(GOOGLE_AUTH_URL);
22
- authUrl.searchParams.set('client_id', clientId);
23
- authUrl.searchParams.set('redirect_uri', redirectUri);
24
- authUrl.searchParams.set('response_type', 'id_token');
25
- authUrl.searchParams.set('scope', 'openid email profile');
26
- authUrl.searchParams.set('nonce', nonce);
27
- authUrl.searchParams.set('prompt', 'select_account');
28
- let stateParam = `provider=${provider}`;
29
- if (state) {
30
- const additionalState = Object.entries(state)
31
- .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
32
- .join('&');
33
- if (additionalState) {
34
- stateParam += `&${additionalState}`;
35
- }
36
- }
37
- authUrl.searchParams.set('state', stateParam);
38
- return authUrl.toString();
39
- }
40
15
  function openOAuthPopup(url) {
41
16
  const width = POPUP_WIDTH;
42
17
  const height = POPUP_HEIGHT;
@@ -48,37 +23,67 @@ function openOAuthPopup(url) {
48
23
  }
49
24
  return authWindow;
50
25
  }
51
- function extractOAuthToken(url) {
52
- const hashParams = new URLSearchParams(url.split('#')[1]);
53
- let idToken = hashParams.get('id_token');
54
- if (!idToken) {
55
- const queryParams = new URLSearchParams(url.split('?')[1]);
56
- idToken = queryParams.get('id_token');
26
+ function generateOAuthNonce(publicKey) {
27
+ return (0, viem_1.sha256)(publicKey).replace(/^0x/, '');
28
+ }
29
+ function buildBackendOAuthUrl(params) {
30
+ const { provider, backendUrl, projectId, publicKey, returnTo } = params;
31
+ if (provider !== exports.OAUTH_PROVIDERS.GOOGLE) {
32
+ throw new Error(`Unsupported OAuth provider: ${provider}`);
57
33
  }
58
- return idToken;
34
+ const oauthUrl = new URL(`${backendUrl}/oauth/google/login`);
35
+ oauthUrl.searchParams.set('project_id', projectId);
36
+ oauthUrl.searchParams.set('pub_key', publicKey.replace(/^0x/, ''));
37
+ oauthUrl.searchParams.set('return_to', returnTo);
38
+ return oauthUrl.toString();
59
39
  }
60
- function pollOAuthPopup(authWindow, originUrl, onSuccess, onError) {
61
- const interval = setInterval(() => {
62
- try {
63
- if (authWindow.closed) {
64
- clearInterval(interval);
65
- onError(new Error('Authentication window was closed'));
66
- return;
67
- }
68
- const url = authWindow.location.href || '';
69
- if (url.startsWith(originUrl)) {
70
- const token = extractOAuthToken(url);
71
- if (token) {
72
- authWindow.close();
73
- clearInterval(interval);
74
- onSuccess(token);
75
- }
76
- }
40
+ function listenForOAuthMessage(authWindow, expectedOrigin, onSuccess, onError) {
41
+ let cleaned = false;
42
+ const handleMessage = (event) => {
43
+ if (event.origin !== expectedOrigin)
44
+ return;
45
+ if (!event.data || typeof event.data !== 'object')
46
+ return;
47
+ if (event.data.type === 'oauth_success') {
48
+ cleanup();
49
+ onSuccess();
77
50
  }
78
- catch {
51
+ else if (event.data.type === 'oauth_error') {
52
+ cleanup();
53
+ onError(new Error(event.data.error || 'OAuth authentication failed'));
54
+ }
55
+ };
56
+ const checkWindowClosed = setInterval(() => {
57
+ if (authWindow.closed) {
58
+ cleanup();
59
+ onError(new Error('Authentication window was closed'));
79
60
  }
80
61
  }, 500);
62
+ const cleanup = () => {
63
+ if (cleaned)
64
+ return;
65
+ cleaned = true;
66
+ window.removeEventListener('message', handleMessage);
67
+ clearInterval(checkWindowClosed);
68
+ };
69
+ window.addEventListener('message', handleMessage);
70
+ return cleanup;
81
71
  }
82
- function generateOAuthNonce(publicKey) {
83
- return (0, viem_1.sha256)(publicKey).replace(/^0x/, '');
72
+ function handleOAuthCallback(successParam = 'oauth_success') {
73
+ const urlParams = new URLSearchParams(window.location.search);
74
+ const isSuccess = urlParams.get(successParam) === 'true';
75
+ const error = urlParams.get('error');
76
+ if (window.opener) {
77
+ if (isSuccess) {
78
+ window.opener.postMessage({ type: 'oauth_success' }, window.location.origin);
79
+ window.close();
80
+ return true;
81
+ }
82
+ if (error) {
83
+ window.opener.postMessage({ type: 'oauth_error', error }, window.location.origin);
84
+ window.close();
85
+ return false;
86
+ }
87
+ }
88
+ return false;
84
89
  }
@@ -1,6 +1,6 @@
1
1
  import { connect as wagmiConnect } from '@wagmi/core/actions';
2
2
  import { createIframeStamper, exportPrivateKey as exportPrivateKeySdk, exportWallet as exportWalletSdk, } from '@zerodev/wallet-core';
3
- import { buildOAuthUrl, generateOAuthNonce, openOAuthPopup, pollOAuthPopup, } from './oauth.js';
3
+ import { buildBackendOAuthUrl, listenForOAuthMessage, openOAuthPopup, } from './oauth.js';
4
4
  /**
5
5
  * Get ZeroDev connector from config
6
6
  */
@@ -61,6 +61,7 @@ export async function loginPasskey(config, parameters) {
61
61
  }
62
62
  /**
63
63
  * Authenticate with OAuth (opens popup)
64
+ * Uses backend OAuth flow where the backend handles PKCE and token exchange
64
65
  */
65
66
  export async function authenticateOAuth(config, parameters) {
66
67
  const connector = parameters.connector ?? getZeroDevConnector(config);
@@ -71,46 +72,36 @@ export async function authenticateOAuth(config, parameters) {
71
72
  if (!wallet)
72
73
  throw new Error('Wallet not initialized');
73
74
  if (!oauthConfig) {
74
- throw new Error('OAuth is not configured. Please provide oauthConfig to zeroDevWallet connector.');
75
+ throw new Error('Wallet not initialized. Please wait for connector setup.');
75
76
  }
76
- // Get client ID for the provider
77
- let clientId = parameters.clientId;
78
- if (!clientId) {
79
- clientId = oauthConfig.googleClientId;
80
- }
81
- if (!clientId) {
82
- throw new Error(`Client ID not configured for ${parameters.provider}`);
83
- }
84
- if (!oauthConfig.redirectUri) {
85
- throw new Error('OAuth redirect URI is not configured.');
86
- }
87
- // Generate nonce from wallet public key
77
+ // Get wallet public key for the OAuth flow
88
78
  const publicKey = await wallet.getPublicKey();
89
79
  if (!publicKey) {
90
80
  throw new Error('Failed to get wallet public key');
91
81
  }
92
- const nonce = generateOAuthNonce(publicKey);
93
- // Build OAuth URL
94
- const oauthUrl = buildOAuthUrl({
82
+ // Build OAuth URL that redirects to backend
83
+ // Use current origin as redirect - SDK auto-detects callback on any page
84
+ const oauthUrl = buildBackendOAuthUrl({
95
85
  provider: parameters.provider,
96
- clientId,
97
- redirectUri: oauthConfig.redirectUri,
98
- nonce,
86
+ backendUrl: oauthConfig.backendUrl,
87
+ projectId: oauthConfig.projectId,
88
+ publicKey,
89
+ returnTo: `${window.location.origin}?oauth_success=true&oauth_provider=${parameters.provider}`,
99
90
  });
100
91
  // Open popup
101
92
  const authWindow = openOAuthPopup(oauthUrl);
102
93
  if (!authWindow) {
103
94
  throw new Error(`Failed to open ${parameters.provider} login window.`);
104
95
  }
105
- // Poll for OAuth completion
96
+ // Listen for OAuth completion via postMessage
106
97
  return new Promise((resolve, reject) => {
107
- pollOAuthPopup(authWindow, window.location.origin, async (idToken) => {
98
+ const cleanup = listenForOAuthMessage(authWindow, window.location.origin, async () => {
108
99
  try {
109
100
  // Complete OAuth authentication with wallet-core
101
+ // The backend has stored the OAuth session in a cookie
110
102
  await wallet.auth({
111
103
  type: 'oauth',
112
104
  provider: parameters.provider,
113
- credential: idToken,
114
105
  });
115
106
  const [session, eoaAccount] = await Promise.all([
116
107
  wallet.getSession(),
@@ -125,7 +116,10 @@ export async function authenticateOAuth(config, parameters) {
125
116
  catch (err) {
126
117
  reject(err);
127
118
  }
128
- }, reject);
119
+ }, (error) => {
120
+ cleanup();
121
+ reject(error);
122
+ });
129
123
  });
130
124
  }
131
125
  /**
@@ -149,7 +143,6 @@ export async function sendOTP(config, parameters) {
149
143
  });
150
144
  return {
151
145
  otpId: result.otpId,
152
- subOrganizationId: result.subOrganizationId,
153
146
  };
154
147
  }
155
148
  /**
@@ -167,7 +160,6 @@ export async function verifyOTP(config, parameters) {
167
160
  mode: 'verifyOtp',
168
161
  otpId: parameters.otpId,
169
162
  otpCode: parameters.code,
170
- subOrganizationId: parameters.subOrganizationId,
171
163
  });
172
164
  const [session, eoaAccount] = await Promise.all([
173
165
  wallet.getSession(),
@@ -194,6 +186,30 @@ export async function refreshSession(config, parameters = {}) {
194
186
  store.getState().setSession(newSession || null);
195
187
  return newSession;
196
188
  }
189
+ /**
190
+ * Get user email
191
+ */
192
+ export async function getUserEmail(config) {
193
+ const connector = getZeroDevConnector(config);
194
+ // @ts-expect-error - getStore is a custom method
195
+ const store = await connector.getStore();
196
+ const wallet = store.getState().wallet;
197
+ if (!wallet)
198
+ throw new Error('Wallet not initialized');
199
+ const oauthConfig = store.getState().oauthConfig;
200
+ if (!oauthConfig) {
201
+ throw new Error('Wallet not initialized. Please wait for connector setup.');
202
+ }
203
+ const session = store.getState().session;
204
+ if (!session) {
205
+ throw new Error('No active session');
206
+ }
207
+ // Call the core SDK method
208
+ return await wallet.client.getUserEmail({
209
+ organizationId: session.organizationId,
210
+ projectId: oauthConfig.projectId,
211
+ });
212
+ }
197
213
  /**
198
214
  * Export wallet
199
215
  */