@turnkey/react-wallet-kit 1.0.0-beta.4 → 1.0.0-beta.6
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/dist/_virtual/_rollupPluginBabelHelpers.mjs +49 -1
- package/dist/_virtual/_rollupPluginBabelHelpers.mjs.map +1 -1
- package/dist/components/auth/OTP.d.ts +1 -0
- package/dist/components/auth/OTP.d.ts.map +1 -1
- package/dist/components/auth/OTP.js +5 -1
- package/dist/components/auth/OTP.js.map +1 -1
- package/dist/components/auth/OTP.mjs +6 -3
- package/dist/components/auth/OTP.mjs.map +1 -1
- package/dist/components/auth/Wallet.d.ts +4 -4
- package/dist/components/auth/Wallet.d.ts.map +1 -1
- package/dist/components/auth/Wallet.js +70 -49
- package/dist/components/auth/Wallet.js.map +1 -1
- package/dist/components/auth/Wallet.mjs +74 -54
- package/dist/components/auth/Wallet.mjs.map +1 -1
- package/dist/components/auth/index.d.ts +5 -1
- package/dist/components/auth/index.d.ts.map +1 -1
- package/dist/components/auth/index.js +98 -12
- package/dist/components/auth/index.js.map +1 -1
- package/dist/components/auth/index.mjs +185 -82
- package/dist/components/auth/index.mjs.map +1 -1
- package/dist/components/export/Export.d.ts +2 -1
- package/dist/components/export/Export.d.ts.map +1 -1
- package/dist/components/export/Export.js +7 -6
- package/dist/components/export/Export.js.map +1 -1
- package/dist/components/export/Export.mjs +7 -6
- package/dist/components/export/Export.mjs.map +1 -1
- package/dist/components/export/ExportWarning.d.ts +2 -1
- package/dist/components/export/ExportWarning.d.ts.map +1 -1
- package/dist/components/export/ExportWarning.js +3 -2
- package/dist/components/export/ExportWarning.js.map +1 -1
- package/dist/components/export/ExportWarning.mjs +3 -2
- package/dist/components/export/ExportWarning.mjs.map +1 -1
- package/dist/components/import/Import.d.ts +7 -3
- package/dist/components/import/Import.d.ts.map +1 -1
- package/dist/components/import/Import.js +74 -37
- package/dist/components/import/Import.js.map +1 -1
- package/dist/components/import/Import.mjs +116 -54
- package/dist/components/import/Import.mjs.map +1 -1
- package/dist/components/user/ConnectWallet.d.ts +8 -0
- package/dist/components/user/ConnectWallet.d.ts.map +1 -0
- package/dist/components/user/{LinkWallet.js → ConnectWallet.js} +18 -18
- package/dist/components/user/ConnectWallet.js.map +1 -0
- package/dist/components/user/{LinkWallet.mjs → ConnectWallet.mjs} +25 -25
- package/dist/components/user/ConnectWallet.mjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/providers/TurnkeyProvider.d.ts +1 -0
- package/dist/providers/TurnkeyProvider.d.ts.map +1 -1
- package/dist/providers/TurnkeyProvider.js +1 -0
- package/dist/providers/TurnkeyProvider.js.map +1 -1
- package/dist/providers/TurnkeyProvider.mjs +1 -0
- package/dist/providers/TurnkeyProvider.mjs.map +1 -1
- package/dist/providers/client/Hook.d.ts +1 -0
- package/dist/providers/client/Hook.d.ts.map +1 -1
- package/dist/providers/client/Hook.js +1 -0
- package/dist/providers/client/Hook.js.map +1 -1
- package/dist/providers/client/Hook.mjs +1 -0
- package/dist/providers/client/Hook.mjs.map +1 -1
- package/dist/providers/client/Provider.d.ts +3 -3
- package/dist/providers/client/Provider.d.ts.map +1 -1
- package/dist/providers/client/Provider.js +1019 -388
- package/dist/providers/client/Provider.js.map +1 -1
- package/dist/providers/client/Provider.mjs +2901 -2162
- package/dist/providers/client/Provider.mjs.map +1 -1
- package/dist/providers/client/Types.d.ts +164 -50
- package/dist/providers/client/Types.d.ts.map +1 -1
- package/dist/providers/client/Types.js +1 -0
- package/dist/providers/client/Types.js.map +1 -1
- package/dist/providers/client/Types.mjs +1 -0
- package/dist/providers/client/Types.mjs.map +1 -1
- package/dist/providers/modal/Hook.d.ts +1 -0
- package/dist/providers/modal/Hook.d.ts.map +1 -1
- package/dist/providers/modal/Hook.js +1 -0
- package/dist/providers/modal/Hook.js.map +1 -1
- package/dist/providers/modal/Hook.mjs +1 -0
- package/dist/providers/modal/Hook.mjs.map +1 -1
- package/dist/providers/modal/Provider.js +1 -1
- package/dist/providers/modal/Provider.mjs +1 -1
- package/dist/providers/modal/Root.d.ts.map +1 -1
- package/dist/providers/modal/Root.js +18 -14
- package/dist/providers/modal/Root.js.map +1 -1
- package/dist/providers/modal/Root.mjs +19 -17
- package/dist/providers/modal/Root.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/base.d.ts +81 -2
- package/dist/types/base.d.ts.map +1 -1
- package/dist/types/base.js +35 -0
- package/dist/types/base.js.map +1 -1
- package/dist/types/base.mjs +31 -1
- package/dist/types/base.mjs.map +1 -1
- package/dist/utils/timers.d.ts +73 -0
- package/dist/utils/timers.d.ts.map +1 -0
- package/dist/utils/timers.js +133 -0
- package/dist/utils/timers.js.map +1 -0
- package/dist/utils/timers.mjs +142 -0
- package/dist/utils/timers.mjs.map +1 -0
- package/dist/{utils.d.ts → utils/utils.d.ts} +9 -3
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/{utils.js → utils/utils.js} +43 -5
- package/dist/utils/utils.js.map +1 -0
- package/dist/{utils.mjs → utils/utils.mjs} +48 -7
- package/dist/utils/utils.mjs.map +1 -0
- package/package.json +4 -4
- package/dist/components/user/LinkWallet.d.ts +0 -8
- package/dist/components/user/LinkWallet.d.ts.map +0 -1
- package/dist/components/user/LinkWallet.js.map +0 -1
- package/dist/components/user/LinkWallet.mjs.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/utils.mjs.map +0 -1
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var sha2 = require('@noble/hashes/sha2');
|
|
6
6
|
var utils$1 = require('@noble/hashes/utils');
|
|
7
|
-
var utils = require('../../utils.js');
|
|
7
|
+
var utils = require('../../utils/utils.js');
|
|
8
|
+
var timers = require('../../utils/timers.js');
|
|
8
9
|
var core = require('@turnkey/core');
|
|
9
10
|
var react = require('react');
|
|
10
11
|
var sdkTypes = require('@turnkey/sdk-types');
|
|
@@ -23,7 +24,7 @@ var UpdatePhoneNumber = require('../../components/user/UpdatePhoneNumber.js');
|
|
|
23
24
|
var UpdateUserName = require('../../components/user/UpdateUserName.js');
|
|
24
25
|
var RemoveOAuthProvider = require('../../components/user/RemoveOAuthProvider.js');
|
|
25
26
|
var RemovePasskey = require('../../components/user/RemovePasskey.js');
|
|
26
|
-
var
|
|
27
|
+
var ConnectWallet = require('../../components/user/ConnectWallet.js');
|
|
27
28
|
var Types = require('./Types.js');
|
|
28
29
|
var OTP = require('../../components/auth/OTP.js');
|
|
29
30
|
var RemoveEmail = require('../../components/user/RemoveEmail.js');
|
|
@@ -32,7 +33,7 @@ var RemovePhoneNumber = require('../../components/user/RemovePhoneNumber.js');
|
|
|
32
33
|
/**
|
|
33
34
|
* Provides Turnkey client authentication, session management, wallet operations, and user profile management
|
|
34
35
|
* for the React Wallet Kit SDK. This context provider encapsulates all core authentication flows (Passkey, Wallet, OTP, OAuth),
|
|
35
|
-
* session lifecycle (creation, expiration, refresh), wallet
|
|
36
|
+
* session lifecycle (creation, expiration, refresh), wallet connecting/import/export, and user profile updates (email, phone, name).
|
|
36
37
|
*
|
|
37
38
|
* The provider automatically initializes the Turnkey client, fetches configuration (including proxy auth config if needed),
|
|
38
39
|
* and synchronizes session and authentication state. It exposes a comprehensive set of methods for authentication flows,
|
|
@@ -41,9 +42,9 @@ var RemovePhoneNumber = require('../../components/user/RemovePhoneNumber.js');
|
|
|
41
42
|
* Features:
|
|
42
43
|
* - Passkey, Wallet, OTP (Email/SMS), and OAuth (Google, Apple, Facebook) authentication and sign-up flows.
|
|
43
44
|
* - Session management: creation, expiration scheduling, refresh, and clearing.
|
|
44
|
-
* - Wallet management: fetch,
|
|
45
|
+
* - Wallet management: fetch, connect, import, export, account management.
|
|
45
46
|
* - User profile management: email, phone, name, OAuth provider, and passkey linking/removal.
|
|
46
|
-
* - Modal-driven UI flows for authentication, wallet
|
|
47
|
+
* - Modal-driven UI flows for authentication, wallet connecting, and profile updates.
|
|
47
48
|
* - Error handling and callback integration for custom error and event responses.
|
|
48
49
|
*
|
|
49
50
|
* Usage:
|
|
@@ -67,6 +68,9 @@ const ClientProvider = ({
|
|
|
67
68
|
const [user, setUser] = react.useState(undefined);
|
|
68
69
|
const [clientState, setClientState] = react.useState();
|
|
69
70
|
const [authState, setAuthState] = react.useState(base.AuthState.Unauthenticated);
|
|
71
|
+
// we use this custom hook to only update the state if the value is different
|
|
72
|
+
// this is so our useEffect that calls `initializeWalletProviderListeners()` only runs when it needs to
|
|
73
|
+
const [walletProviders, setWalletProviders] = utils.useWalletProviderState();
|
|
70
74
|
const expiryTimeoutsRef = react.useRef({});
|
|
71
75
|
const proxyAuthConfigRef = react.useRef(null);
|
|
72
76
|
const [allSessions, setAllSessions] = react.useState(undefined);
|
|
@@ -74,78 +78,6 @@ const ClientProvider = ({
|
|
|
74
78
|
pushPage,
|
|
75
79
|
closeModal
|
|
76
80
|
} = Hook.useModal();
|
|
77
|
-
react.useEffect(() => {
|
|
78
|
-
if (proxyAuthConfigRef.current) return;
|
|
79
|
-
// Only fetch the proxy auth config once. Use that to build the master config.
|
|
80
|
-
const fetchProxyAuthConfig = async () => {
|
|
81
|
-
try {
|
|
82
|
-
let proxyAuthConfig;
|
|
83
|
-
if (config.authProxyConfigId) {
|
|
84
|
-
// Only fetch the proxy auth config if we have an authProxyId. This is a way for devs to explicitly disable the proxy auth.
|
|
85
|
-
proxyAuthConfig = await core.getAuthProxyConfig(config.authProxyConfigId, config.authProxyUrl);
|
|
86
|
-
proxyAuthConfigRef.current = proxyAuthConfig;
|
|
87
|
-
}
|
|
88
|
-
setMasterConfig(buildConfig(proxyAuthConfig));
|
|
89
|
-
} catch {
|
|
90
|
-
setClientState(base.ClientState.Error);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
fetchProxyAuthConfig();
|
|
94
|
-
}, []);
|
|
95
|
-
react.useEffect(() => {
|
|
96
|
-
// Start the client initialization process once we have the master config.
|
|
97
|
-
if (!masterConfig) return;
|
|
98
|
-
initializeClient();
|
|
99
|
-
}, [masterConfig]);
|
|
100
|
-
react.useEffect(() => {
|
|
101
|
-
// Handle changes to the passed in config prop -- update the master config
|
|
102
|
-
// If the proxyAuthConfigRef is already set, we don't need to fetch it again. Rebuild the master config with the updated config and stored proxyAuthConfig
|
|
103
|
-
if (!proxyAuthConfigRef.current && config.authProxyConfigId) return;
|
|
104
|
-
setMasterConfig(buildConfig(proxyAuthConfigRef.current ?? undefined));
|
|
105
|
-
}, [config, proxyAuthConfigRef.current]);
|
|
106
|
-
/**
|
|
107
|
-
* @internal
|
|
108
|
-
* we use debouncedFetchWallets() to prevent multiple rapid wallet events
|
|
109
|
-
* like accountsChanged and disconnect from triggering fetchWallets repeatedly
|
|
110
|
-
* it must be defined outside the useEffect so all event listeners share the same
|
|
111
|
-
* debounced function instance - otherwise, a new one would be created on every render
|
|
112
|
-
*/
|
|
113
|
-
const debouncedFetchWallets = utils.useDebouncedCallback(fetchWallets, 100);
|
|
114
|
-
react.useEffect(() => {
|
|
115
|
-
if (!client) return;
|
|
116
|
-
let cleanup = () => {};
|
|
117
|
-
initializeProviders(getWalletProviders, debouncedFetchWallets).then(fn => {
|
|
118
|
-
cleanup = fn;
|
|
119
|
-
}).catch(err => {
|
|
120
|
-
console.error("Failed to init providers:", err);
|
|
121
|
-
});
|
|
122
|
-
return () => {
|
|
123
|
-
cleanup();
|
|
124
|
-
};
|
|
125
|
-
}, [client, debouncedFetchWallets]);
|
|
126
|
-
react.useEffect(() => {
|
|
127
|
-
// authState must be consistent with session state. We found during testing that there are cases where the session and authState can be out of sync in very rare edge cases.
|
|
128
|
-
// This will ensure that they are always in sync and remove the need to setAuthState manually in other places.
|
|
129
|
-
if (session && utils.isValidSession(session)) {
|
|
130
|
-
setAuthState(base.AuthState.Authenticated);
|
|
131
|
-
} else {
|
|
132
|
-
setAuthState(base.AuthState.Unauthenticated);
|
|
133
|
-
}
|
|
134
|
-
}, [session]);
|
|
135
|
-
react.useEffect(() => {
|
|
136
|
-
// This will handle any redirect based oAuth. It then initializes the session. This is the last step before client is considered "ready"
|
|
137
|
-
if (!client || !masterConfig) return;
|
|
138
|
-
completeRedirectOauth().finally(() => {
|
|
139
|
-
clearSessionTimeouts();
|
|
140
|
-
initializeSessions().finally(() => {
|
|
141
|
-
// Set the client state to ready only after all initializations are done.
|
|
142
|
-
setClientState(base.ClientState.Ready);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
return () => {
|
|
146
|
-
clearSessionTimeouts();
|
|
147
|
-
};
|
|
148
|
-
}, [client]);
|
|
149
81
|
const completeRedirectOauth = async () => {
|
|
150
82
|
// Check for either hash or search parameters that could indicate an OAuth redirect
|
|
151
83
|
if (window.location.hash || window.location.search) {
|
|
@@ -161,6 +93,7 @@ const ClientProvider = ({
|
|
|
161
93
|
const flow = stateParams.get("flow");
|
|
162
94
|
const publicKey = stateParams.get("publicKey");
|
|
163
95
|
const openModal = stateParams.get("openModal");
|
|
96
|
+
const sessionKey = stateParams.get("sessionKey");
|
|
164
97
|
if (provider === "facebook" && flow === "redirect" && publicKey) {
|
|
165
98
|
// We have all the required parameters for a Facebook PKCE flow
|
|
166
99
|
const clientId = masterConfig?.auth?.oauthConfig?.facebookClientId;
|
|
@@ -185,8 +118,13 @@ const ClientProvider = ({
|
|
|
185
118
|
await completeOauth({
|
|
186
119
|
oidcToken,
|
|
187
120
|
publicKey,
|
|
188
|
-
providerName: "facebook"
|
|
121
|
+
providerName: "facebook",
|
|
122
|
+
...(sessionKey && {
|
|
123
|
+
sessionKey
|
|
124
|
+
})
|
|
189
125
|
});
|
|
126
|
+
// Clean up the URL after processing
|
|
127
|
+
window.history.replaceState(null, document.title, window.location.pathname);
|
|
190
128
|
resolve();
|
|
191
129
|
} catch (err) {
|
|
192
130
|
reject(err);
|
|
@@ -209,6 +147,114 @@ const ClientProvider = ({
|
|
|
209
147
|
});
|
|
210
148
|
}
|
|
211
149
|
}
|
|
150
|
+
if (provider === "discord" && flow === "redirect" && publicKey) {
|
|
151
|
+
const clientId = masterConfig?.auth?.oauthConfig?.discordClientId;
|
|
152
|
+
const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
|
|
153
|
+
const verifier = sessionStorage.getItem("discord_verifier");
|
|
154
|
+
const nonce = stateParams.get("nonce");
|
|
155
|
+
const sessionKey = stateParams.get("sessionKey");
|
|
156
|
+
if (clientId && redirectURI && verifier && nonce) {
|
|
157
|
+
await new Promise((resolve, reject) => {
|
|
158
|
+
pushPage({
|
|
159
|
+
key: `Discord OAuth`,
|
|
160
|
+
content: jsxRuntime.jsx(Action.ActionPage, {
|
|
161
|
+
title: `Authenticating with Discord...`,
|
|
162
|
+
action: async () => {
|
|
163
|
+
try {
|
|
164
|
+
const resp = await client?.httpClient.proxyOAuth2Authenticate({
|
|
165
|
+
provider: "OAUTH2_PROVIDER_DISCORD",
|
|
166
|
+
authCode: code,
|
|
167
|
+
redirectUri: redirectURI,
|
|
168
|
+
codeVerifier: verifier,
|
|
169
|
+
nonce: nonce
|
|
170
|
+
});
|
|
171
|
+
sessionStorage.removeItem("discord_verifier");
|
|
172
|
+
const oidcToken = resp?.oidcToken;
|
|
173
|
+
if (!oidcToken) {
|
|
174
|
+
throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
|
|
175
|
+
}
|
|
176
|
+
await completeOauth({
|
|
177
|
+
oidcToken,
|
|
178
|
+
publicKey,
|
|
179
|
+
providerName: "discord",
|
|
180
|
+
...(sessionKey && {
|
|
181
|
+
sessionKey
|
|
182
|
+
})
|
|
183
|
+
});
|
|
184
|
+
// Clean up the URL after processing
|
|
185
|
+
window.history.replaceState(null, document.title, window.location.pathname);
|
|
186
|
+
resolve(null);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
reject(err);
|
|
189
|
+
if (callbacks?.onError) {
|
|
190
|
+
callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Discord authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
|
|
195
|
+
size: "3x",
|
|
196
|
+
icon: freeBrandsSvgIcons.faDiscord
|
|
197
|
+
})
|
|
198
|
+
}),
|
|
199
|
+
showTitle: false
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (provider === "twitter" && flow === "redirect" && publicKey) {
|
|
205
|
+
const clientId = masterConfig?.auth?.oauthConfig?.xClientId;
|
|
206
|
+
const redirectURI = masterConfig?.auth?.oauthConfig?.oauthRedirectUri;
|
|
207
|
+
const verifier = sessionStorage.getItem("twitter_verifier");
|
|
208
|
+
const nonce = stateParams.get("nonce");
|
|
209
|
+
const sessionKey = stateParams.get("sessionKey");
|
|
210
|
+
if (clientId && redirectURI && verifier && nonce) {
|
|
211
|
+
await new Promise((resolve, reject) => {
|
|
212
|
+
pushPage({
|
|
213
|
+
key: `Twitter OAuth`,
|
|
214
|
+
content: jsxRuntime.jsx(Action.ActionPage, {
|
|
215
|
+
title: `Authenticating with Twitter...`,
|
|
216
|
+
action: async () => {
|
|
217
|
+
try {
|
|
218
|
+
const resp = await client?.httpClient.proxyOAuth2Authenticate({
|
|
219
|
+
provider: "OAUTH2_PROVIDER_X",
|
|
220
|
+
authCode: code,
|
|
221
|
+
redirectUri: redirectURI,
|
|
222
|
+
codeVerifier: verifier,
|
|
223
|
+
nonce: nonce
|
|
224
|
+
});
|
|
225
|
+
sessionStorage.removeItem("twitter_verifier");
|
|
226
|
+
const oidcToken = resp?.oidcToken;
|
|
227
|
+
if (!oidcToken) {
|
|
228
|
+
throw new sdkTypes.TurnkeyError("Missing OIDC token", sdkTypes.TurnkeyErrorCodes.OAUTH_LOGIN_ERROR);
|
|
229
|
+
}
|
|
230
|
+
await completeOauth({
|
|
231
|
+
oidcToken,
|
|
232
|
+
publicKey,
|
|
233
|
+
providerName: "twitter",
|
|
234
|
+
...(sessionKey && {
|
|
235
|
+
sessionKey
|
|
236
|
+
})
|
|
237
|
+
});
|
|
238
|
+
// Clean up the URL after processing
|
|
239
|
+
window.history.replaceState(null, document.title, window.location.pathname);
|
|
240
|
+
resolve(null);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
reject(err);
|
|
243
|
+
if (callbacks?.onError) {
|
|
244
|
+
callbacks.onError(err instanceof sdkTypes.TurnkeyError ? err : new sdkTypes.TurnkeyError("Twitter authentication failed", sdkTypes.TurnkeyErrorCodes.OAUTH_SIGNUP_ERROR, err));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
icon: jsxRuntime.jsx(reactFontawesome.FontAwesomeIcon, {
|
|
249
|
+
size: "3x",
|
|
250
|
+
icon: freeBrandsSvgIcons.faXTwitter
|
|
251
|
+
})
|
|
252
|
+
}),
|
|
253
|
+
showTitle: false
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
212
258
|
}
|
|
213
259
|
}
|
|
214
260
|
// Handle Google/Apple redirects (uses hash with id_token)
|
|
@@ -220,7 +266,8 @@ const ClientProvider = ({
|
|
|
220
266
|
provider,
|
|
221
267
|
flow,
|
|
222
268
|
publicKey,
|
|
223
|
-
openModal
|
|
269
|
+
openModal,
|
|
270
|
+
sessionKey
|
|
224
271
|
} = utils.parseOAuthRedirect(hash);
|
|
225
272
|
if (idToken && flow === "redirect" && publicKey) {
|
|
226
273
|
if (openModal === "true") {
|
|
@@ -252,7 +299,10 @@ const ClientProvider = ({
|
|
|
252
299
|
publicKey,
|
|
253
300
|
...(provider ? {
|
|
254
301
|
providerName: provider
|
|
255
|
-
} : {})
|
|
302
|
+
} : {}),
|
|
303
|
+
...(sessionKey && {
|
|
304
|
+
sessionKey
|
|
305
|
+
})
|
|
256
306
|
});
|
|
257
307
|
resolve(null);
|
|
258
308
|
} catch (err) {
|
|
@@ -292,11 +342,13 @@ const ClientProvider = ({
|
|
|
292
342
|
passkeyAuthEnabled: config.auth?.methods?.passkeyAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("passkey"),
|
|
293
343
|
walletAuthEnabled: config.auth?.methods?.walletAuthEnabled ?? proxyAuthConfig?.enabledProviders.includes("wallet"),
|
|
294
344
|
googleOauthEnabled: config.auth?.methods?.googleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("google"),
|
|
345
|
+
xOauthEnabled: config.auth?.methods?.xOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("x"),
|
|
346
|
+
discordOauthEnabled: config.auth?.methods?.discordOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("discord"),
|
|
295
347
|
appleOauthEnabled: config.auth?.methods?.appleOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("apple"),
|
|
296
348
|
facebookOauthEnabled: config.auth?.methods?.facebookOauthEnabled ?? proxyAuthConfig?.enabledProviders.includes("facebook")
|
|
297
349
|
};
|
|
298
350
|
// Set a default ordering for the oAuth methods
|
|
299
|
-
const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
|
|
351
|
+
const oauthOrder = config.auth?.oauthOrder ?? ["google", "apple", "x", "discord", "facebook"].filter(provider => resolvedMethods[`${provider}OauthEnabled`]);
|
|
300
352
|
// Set a default ordering for the overall auth methods
|
|
301
353
|
const methodOrder = config.auth?.methodOrder ?? [oauthOrder.length > 0 ? "socials" : null, resolvedMethods.emailOtpAuthEnabled ? "email" : null, resolvedMethods.smsOtpAuthEnabled ? "sms" : null, resolvedMethods.passkeyAuthEnabled ? "passkey" : null, resolvedMethods.walletAuthEnabled ? "wallet" : null].filter(Boolean);
|
|
302
354
|
return {
|
|
@@ -438,14 +490,86 @@ const ClientProvider = ({
|
|
|
438
490
|
};
|
|
439
491
|
/**
|
|
440
492
|
* @internal
|
|
441
|
-
*
|
|
493
|
+
* Attach listeners for connected wallet providers so we can refresh state on changes.
|
|
494
|
+
*
|
|
495
|
+
* - Ethereum: listens for disconnect and chain switches to trigger a refresh.
|
|
496
|
+
* - Solana: listens for disconnect via Wallet Standard `change` events to trigger a refresh.
|
|
497
|
+
* - WalletConnect: listens for disconnect via our custom wrapper’s `change` event to trigger a refresh.
|
|
442
498
|
*
|
|
443
|
-
*
|
|
444
|
-
* -
|
|
445
|
-
* -
|
|
499
|
+
* Notes:
|
|
500
|
+
* - Only providers that are connected are bound.
|
|
501
|
+
* - WalletConnect is excluded from the “native” paths and handled via its unified `change` event.
|
|
502
|
+
*
|
|
503
|
+
* @param walletProviders - Discovered providers; only connected ones are bound.
|
|
504
|
+
* @param onWalletsChanged - Invoked when a relevant provider event occurs.
|
|
505
|
+
* @returns Cleanup function that removes all listeners registered by this call.
|
|
506
|
+
*/
|
|
507
|
+
async function initializeWalletProviderListeners(walletProviders, onWalletsChanged) {
|
|
508
|
+
if (walletProviders.length === 0) return () => {};
|
|
509
|
+
const cleanups = [];
|
|
510
|
+
// we only want to initialize these listeners for connected walletProviders
|
|
511
|
+
const nativeOnly = provider => provider.interfaceType !== core.WalletInterfaceType.WalletConnect;
|
|
512
|
+
const ethProviders = masterConfig?.walletConfig?.chains.ethereum?.native ? walletProviders.filter(provider => provider.chainInfo.namespace === core.Chain.Ethereum && nativeOnly(provider) && provider.connectedAddresses.length > 0) : [];
|
|
513
|
+
const solProviders = masterConfig?.walletConfig?.chains.solana?.native ? walletProviders.filter(provider => provider.chainInfo.namespace === core.Chain.Solana && nativeOnly(provider) && provider.connectedAddresses.length > 0) : [];
|
|
514
|
+
// we exclude WalletConnect from the native event wiring
|
|
515
|
+
// this is because WC is handled separately with a custom wrapper’s
|
|
516
|
+
// `change` event
|
|
517
|
+
const wcProviders = walletProviders.filter(p => p.interfaceType === core.WalletInterfaceType.WalletConnect && p.connectedAddresses.length > 0);
|
|
518
|
+
function attachEthereumListeners(provider, onWalletsChanged) {
|
|
519
|
+
if (typeof provider.on !== "function") return;
|
|
520
|
+
const handleChainChanged = _chainId => onWalletsChanged();
|
|
521
|
+
const handleAccountsChanged = accounts => {
|
|
522
|
+
if (accounts.length === 0) onWalletsChanged();
|
|
523
|
+
};
|
|
524
|
+
const handleDisconnect = () => onWalletsChanged();
|
|
525
|
+
provider.on("chainChanged", handleChainChanged);
|
|
526
|
+
provider.on("accountsChanged", handleAccountsChanged);
|
|
527
|
+
provider.on("disconnect", handleDisconnect);
|
|
528
|
+
return () => {
|
|
529
|
+
provider.removeListener("chainChanged", handleChainChanged);
|
|
530
|
+
provider.removeListener("accountsChanged", handleAccountsChanged);
|
|
531
|
+
provider.removeListener("disconnect", handleDisconnect);
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function attachSolanaListeners(provider, onWalletsChanged) {
|
|
535
|
+
const cleanups = [];
|
|
536
|
+
const walletEvents = provider?.features?.["standard:events"];
|
|
537
|
+
if (walletEvents?.on) {
|
|
538
|
+
const offChange = walletEvents.on("change", _evt => {
|
|
539
|
+
onWalletsChanged();
|
|
540
|
+
});
|
|
541
|
+
cleanups.push(offChange);
|
|
542
|
+
}
|
|
543
|
+
return () => cleanups.forEach(fn => fn());
|
|
544
|
+
}
|
|
545
|
+
ethProviders.forEach(p => {
|
|
546
|
+
const cleanup = attachEthereumListeners(p.provider, onWalletsChanged);
|
|
547
|
+
if (cleanup) cleanups.push(cleanup);
|
|
548
|
+
});
|
|
549
|
+
solProviders.forEach(p => {
|
|
550
|
+
const cleanup = attachSolanaListeners(p.provider, onWalletsChanged);
|
|
551
|
+
if (cleanup) cleanups.push(cleanup);
|
|
552
|
+
});
|
|
553
|
+
wcProviders.forEach(p => {
|
|
554
|
+
const standardEvents = p.provider?.features?.["standard:events"];
|
|
555
|
+
if (standardEvents?.on) {
|
|
556
|
+
const unsubscribe = standardEvents.on("change", onWalletsChanged);
|
|
557
|
+
cleanups.push(unsubscribe);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
return () => {
|
|
561
|
+
cleanups.forEach(remove => remove());
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* @internal
|
|
566
|
+
* Schedules a session expiration and warning timer for the given session key.
|
|
567
|
+
*
|
|
568
|
+
* - Sets up two timers: one for warning before expiry and one for actual expiry.
|
|
569
|
+
* - Uses capped timeouts under the hood so delays > 24.8 days are safe (see utils/timers.ts).
|
|
446
570
|
*
|
|
447
571
|
* @param params.sessionKey - The key of the session to schedule expiration for.
|
|
448
|
-
* @param params.expiry - The expiration time in seconds
|
|
572
|
+
* @param params.expiry - The expiration time in seconds since epoch.
|
|
449
573
|
* @throws {TurnkeyError} If an error occurs while scheduling the session expiration.
|
|
450
574
|
*/
|
|
451
575
|
async function scheduleSessionExpiration(params) {
|
|
@@ -454,21 +578,18 @@ const ClientProvider = ({
|
|
|
454
578
|
expiry
|
|
455
579
|
} = params;
|
|
456
580
|
try {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
delete expiryTimeoutsRef.current[`${sessionKey}-warning`];
|
|
465
|
-
}
|
|
466
|
-
const timeUntilExpiry = expiry * 1000 - Date.now();
|
|
581
|
+
const warnKey = `${sessionKey}-warning`;
|
|
582
|
+
// Replace any prior timers for this session
|
|
583
|
+
timers.clearKey(expiryTimeoutsRef.current, sessionKey);
|
|
584
|
+
timers.clearKey(expiryTimeoutsRef.current, warnKey);
|
|
585
|
+
const now = Date.now();
|
|
586
|
+
const expiryMs = expiry * 1000;
|
|
587
|
+
const timeUntilExpiry = expiryMs - now;
|
|
467
588
|
const beforeExpiry = async () => {
|
|
468
589
|
const activeSession = await getSession();
|
|
469
|
-
if (!activeSession && expiryTimeoutsRef.current[
|
|
470
|
-
|
|
471
|
-
expiryTimeoutsRef.current
|
|
590
|
+
if (!activeSession && expiryTimeoutsRef.current[warnKey]) {
|
|
591
|
+
// Keep nudging until session materializes (short 10s timer is fine)
|
|
592
|
+
timers.setTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, 10_000);
|
|
472
593
|
return;
|
|
473
594
|
}
|
|
474
595
|
const session = await getSession({
|
|
@@ -496,27 +617,36 @@ const ClientProvider = ({
|
|
|
496
617
|
if ((await getActiveSessionKey()) === sessionKey) {
|
|
497
618
|
setSession(undefined);
|
|
498
619
|
}
|
|
499
|
-
setAllSessions(
|
|
500
|
-
if (!
|
|
501
|
-
const
|
|
502
|
-
...
|
|
620
|
+
setAllSessions(prev => {
|
|
621
|
+
if (!prev) return prev;
|
|
622
|
+
const next = {
|
|
623
|
+
...prev
|
|
503
624
|
};
|
|
504
|
-
delete
|
|
505
|
-
return
|
|
625
|
+
delete next[sessionKey];
|
|
626
|
+
return next;
|
|
506
627
|
});
|
|
507
628
|
await clearSession({
|
|
508
629
|
sessionKey
|
|
509
630
|
});
|
|
510
|
-
|
|
511
|
-
|
|
631
|
+
// Remove timers for this session
|
|
632
|
+
timers.clearKey(expiryTimeoutsRef.current, sessionKey);
|
|
633
|
+
timers.clearKey(expiryTimeoutsRef.current, warnKey);
|
|
512
634
|
await logout();
|
|
513
635
|
};
|
|
514
|
-
|
|
515
|
-
|
|
636
|
+
// Already expired → expire immediately
|
|
637
|
+
if (timeUntilExpiry <= 0) {
|
|
638
|
+
await expireSession();
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
// Warning timer (if threshold is in the future)
|
|
642
|
+
const warnAt = expiryMs - utils.SESSION_WARNING_THRESHOLD_MS;
|
|
643
|
+
if (warnAt <= now) {
|
|
644
|
+
void beforeExpiry(); // fire-and-forget is fine
|
|
516
645
|
} else {
|
|
517
|
-
expiryTimeoutsRef.current
|
|
646
|
+
timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, warnKey, beforeExpiry, warnAt - now);
|
|
518
647
|
}
|
|
519
|
-
|
|
648
|
+
// Actual expiry timer (safe for long delays)
|
|
649
|
+
timers.setCappedTimeoutInMap(expiryTimeoutsRef.current, sessionKey, expireSession, timeUntilExpiry);
|
|
520
650
|
} catch (error) {
|
|
521
651
|
if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
|
|
522
652
|
callbacks?.onError?.(error);
|
|
@@ -526,25 +656,25 @@ const ClientProvider = ({
|
|
|
526
656
|
}
|
|
527
657
|
}
|
|
528
658
|
/**
|
|
529
|
-
* Clears all scheduled session
|
|
659
|
+
* Clears all scheduled session timers (warning + expiry).
|
|
530
660
|
*
|
|
531
|
-
* -
|
|
532
|
-
* -
|
|
533
|
-
* - All timeouts stored in `expiryTimeoutsRef` are cleared and the reference is reset.
|
|
661
|
+
* - Removes all active timers managed by this client.
|
|
662
|
+
* - Useful on re-init or logout to avoid stale timers.
|
|
534
663
|
*
|
|
535
|
-
* @throws {TurnkeyError} If an error occurs while clearing the
|
|
664
|
+
* @throws {TurnkeyError} If an error occurs while clearing the timers.
|
|
536
665
|
*/
|
|
537
|
-
function clearSessionTimeouts() {
|
|
666
|
+
function clearSessionTimeouts(sessionKeys) {
|
|
538
667
|
try {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
668
|
+
if (sessionKeys) {
|
|
669
|
+
timers.clearKeys(expiryTimeoutsRef.current, sessionKeys);
|
|
670
|
+
} else {
|
|
671
|
+
timers.clearAll(expiryTimeoutsRef.current); // clears & deletes everything
|
|
672
|
+
}
|
|
543
673
|
} catch (error) {
|
|
544
674
|
if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
|
|
545
675
|
callbacks?.onError?.(error);
|
|
546
676
|
} else {
|
|
547
|
-
callbacks?.onError?.(new sdkTypes.TurnkeyError(
|
|
677
|
+
callbacks?.onError?.(new sdkTypes.TurnkeyError("Failed to clear session timeouts", sdkTypes.TurnkeyErrorCodes.CLEAR_SESSION_TIMEOUTS_ERROR, error));
|
|
548
678
|
}
|
|
549
679
|
}
|
|
550
680
|
}
|
|
@@ -560,7 +690,12 @@ const ClientProvider = ({
|
|
|
560
690
|
* @returns A void promise.
|
|
561
691
|
* @throws {TurnkeyError} If the client is not initialized or if there is an error during the process.
|
|
562
692
|
*/
|
|
563
|
-
const handlePostAuth = async
|
|
693
|
+
const handlePostAuth = async params => {
|
|
694
|
+
const {
|
|
695
|
+
method,
|
|
696
|
+
action,
|
|
697
|
+
identifier
|
|
698
|
+
} = params;
|
|
564
699
|
try {
|
|
565
700
|
const sessionKey = await getActiveSessionKey();
|
|
566
701
|
const session = await getSession({
|
|
@@ -577,6 +712,12 @@ const ClientProvider = ({
|
|
|
577
712
|
setAllSessions(allSessions);
|
|
578
713
|
await refreshWallets();
|
|
579
714
|
await refreshUser();
|
|
715
|
+
callbacks?.onAuthenticationSuccess?.({
|
|
716
|
+
session,
|
|
717
|
+
method,
|
|
718
|
+
action,
|
|
719
|
+
identifier
|
|
720
|
+
});
|
|
580
721
|
} catch (error) {
|
|
581
722
|
if (error instanceof sdkTypes.TurnkeyError || error instanceof sdkTypes.TurnkeyNetworkError) {
|
|
582
723
|
callbacks?.onError?.(error);
|
|
@@ -590,42 +731,58 @@ const ClientProvider = ({
|
|
|
590
731
|
* Handles the post-logout flow.
|
|
591
732
|
*
|
|
592
733
|
* - This function is called after a successful logout or session clear.
|
|
593
|
-
* - It clears all scheduled session expiration and warning timeouts to prevent memory leaks.
|
|
594
|
-
* - It resets the session state, removes
|
|
734
|
+
* - It clears all scheduled session expiration and warning timeouts associated to the session key to prevent memory leaks.
|
|
735
|
+
* - It resets the session state, removes user data from memory, the logged out session from all sessions state, and clears the wallets list.
|
|
595
736
|
* - This ensures that all sensitive information is removed from the provider state after logout.
|
|
596
737
|
* - Called internally after logout or when all sessions are cleared.
|
|
597
738
|
*
|
|
598
739
|
* @returns void
|
|
599
740
|
* @throws {TurnkeyError} If there is an error during the post-logout process.
|
|
600
741
|
*/
|
|
601
|
-
const handlePostLogout =
|
|
742
|
+
const handlePostLogout = sessionKey => {
|
|
602
743
|
try {
|
|
603
|
-
clearSessionTimeouts();
|
|
744
|
+
clearSessionTimeouts(sessionKey ? [sessionKey, `${sessionKey}-warning`] : undefined);
|
|
745
|
+
setAllSessions(prev => {
|
|
746
|
+
if (!prev) return prev;
|
|
747
|
+
if (sessionKey) {
|
|
748
|
+
const next = {
|
|
749
|
+
...prev
|
|
750
|
+
};
|
|
751
|
+
delete next[sessionKey];
|
|
752
|
+
return next;
|
|
753
|
+
}
|
|
754
|
+
return {};
|
|
755
|
+
});
|
|
604
756
|
setSession(undefined);
|
|
605
|
-
setAllSessions(undefined);
|
|
606
757
|
setUser(undefined);
|
|
607
758
|
setWallets([]);
|
|
608
759
|
} catch (error) {
|
|
609
760
|
callbacks?.onError?.(new sdkTypes.TurnkeyError(`Failed to initialize sessions`, sdkTypes.TurnkeyErrorCodes.HANDLE_POST_LOGOUT_ERROR, error));
|
|
610
761
|
}
|
|
611
762
|
};
|
|
612
|
-
|
|
763
|
+
const createPasskey = react.useCallback(async params => {
|
|
613
764
|
if (!client) {
|
|
614
765
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
615
766
|
}
|
|
616
767
|
return utils.withTurnkeyErrorHandling(() => client.createPasskey({
|
|
617
768
|
...params
|
|
618
769
|
}), callbacks, "Failed to create passkey");
|
|
619
|
-
}
|
|
620
|
-
|
|
770
|
+
}, [client, callbacks]);
|
|
771
|
+
const logout = react.useCallback(async params => {
|
|
621
772
|
if (!client) {
|
|
622
773
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
623
774
|
}
|
|
624
|
-
utils.withTurnkeyErrorHandling(() =>
|
|
625
|
-
|
|
775
|
+
await utils.withTurnkeyErrorHandling(async () => {
|
|
776
|
+
// If no sessionKey is provided, we try to get the active one.
|
|
777
|
+
let sessionKey = params?.sessionKey;
|
|
778
|
+
if (!sessionKey) sessionKey = await getActiveSessionKey();
|
|
779
|
+
await client.logout(params);
|
|
780
|
+
// We only handle post logout if the sessionKey is defined since that means we actually logged out of a session.
|
|
781
|
+
if (sessionKey) handlePostLogout(sessionKey);
|
|
782
|
+
}, callbacks, "Failed to logout");
|
|
626
783
|
return;
|
|
627
|
-
}
|
|
628
|
-
|
|
784
|
+
}, [client, callbacks]);
|
|
785
|
+
const loginWithPasskey = react.useCallback(async params => {
|
|
629
786
|
if (!client) {
|
|
630
787
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
631
788
|
}
|
|
@@ -635,11 +792,15 @@ const ClientProvider = ({
|
|
|
635
792
|
expirationSeconds
|
|
636
793
|
}), callbacks, "Failed to login with passkey");
|
|
637
794
|
if (res) {
|
|
638
|
-
await handlePostAuth(
|
|
795
|
+
await handlePostAuth({
|
|
796
|
+
method: base.AuthMethod.Passkey,
|
|
797
|
+
action: sdkTypes.AuthAction.LOGIN,
|
|
798
|
+
identifier: res.credentialId
|
|
799
|
+
});
|
|
639
800
|
}
|
|
640
801
|
return res;
|
|
641
|
-
}
|
|
642
|
-
|
|
802
|
+
}, [client, callbacks]);
|
|
803
|
+
const signUpWithPasskey = react.useCallback(async params => {
|
|
643
804
|
if (!client) {
|
|
644
805
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
645
806
|
}
|
|
@@ -670,95 +831,61 @@ const ClientProvider = ({
|
|
|
670
831
|
expirationSeconds
|
|
671
832
|
}), callbacks, "Failed to sign up with passkey");
|
|
672
833
|
if (res) {
|
|
673
|
-
await handlePostAuth(
|
|
834
|
+
await handlePostAuth({
|
|
835
|
+
method: base.AuthMethod.Passkey,
|
|
836
|
+
action: sdkTypes.AuthAction.SIGNUP,
|
|
837
|
+
identifier: res.credentialId
|
|
838
|
+
});
|
|
674
839
|
}
|
|
675
840
|
return res;
|
|
676
|
-
}
|
|
677
|
-
|
|
841
|
+
}, [client, callbacks]);
|
|
842
|
+
const getWalletProviders = react.useCallback(async chain => {
|
|
678
843
|
if (!client) {
|
|
679
844
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
680
845
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
846
|
+
const newProviders = await client.getWalletProviders(chain);
|
|
847
|
+
// we update state with the latest providers
|
|
848
|
+
// we keep this state so that initializeWalletProviderListeners() re-runs
|
|
849
|
+
// whenever the list of connected providers changes
|
|
850
|
+
// this ensures we attach disconnect listeners for each connected provider
|
|
851
|
+
setWalletProviders(newProviders);
|
|
852
|
+
return newProviders;
|
|
853
|
+
}, [client, callbacks]);
|
|
854
|
+
const connectWalletAccount = react.useCallback(async walletProvider => {
|
|
684
855
|
if (!client) {
|
|
685
856
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
686
857
|
}
|
|
687
858
|
await client.connectWalletAccount(walletProvider);
|
|
859
|
+
// this will update our walletProvider state
|
|
688
860
|
await refreshWallets();
|
|
689
|
-
}
|
|
690
|
-
|
|
861
|
+
}, [client, callbacks]);
|
|
862
|
+
const disconnectWalletAccount = react.useCallback(async walletProvider => {
|
|
691
863
|
if (!client) {
|
|
692
864
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
693
865
|
}
|
|
694
866
|
await client.disconnectWalletAccount(walletProvider);
|
|
695
|
-
// we only refresh the wallets if
|
|
696
|
-
//
|
|
697
|
-
// actually being logged in
|
|
698
|
-
|
|
867
|
+
// we only refresh the wallets if:
|
|
868
|
+
// 1. there is an active session. This is needed because for WalletConnect
|
|
869
|
+
// you can disconnect a wallet before actually being logged in
|
|
870
|
+
//
|
|
871
|
+
// 2. it was a WalletConnect provider that we just disconnected. Since
|
|
872
|
+
// native providers emit a disconnect event which will already refresh
|
|
873
|
+
// the wallets. This event is triggered in `initializeWalletProviderListeners()`
|
|
874
|
+
if (session && walletProvider.interfaceType === core.WalletInterfaceType.WalletConnect) {
|
|
875
|
+
// this will update our walletProvider state
|
|
699
876
|
await refreshWallets();
|
|
700
877
|
}
|
|
701
|
-
}
|
|
702
|
-
|
|
878
|
+
}, [client, callbacks]);
|
|
879
|
+
const switchWalletAccountChain = react.useCallback(async params => {
|
|
703
880
|
if (!client) {
|
|
704
881
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
705
882
|
}
|
|
706
|
-
await client.
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
async function initializeProviders(getWalletProviders, onWalletsChanged) {
|
|
710
|
-
const cleanups = [];
|
|
711
|
-
// we only want to initialize these listeners for native wallet providers
|
|
712
|
-
// and not WalletConnect
|
|
713
|
-
const nativeOnly = p => p.interfaceType !== core.WalletInterfaceType.WalletConnect;
|
|
714
|
-
const ethProviders = masterConfig?.walletConfig?.chains.ethereum?.native ? (await getWalletProviders(core.Chain.Ethereum)).filter(nativeOnly) : [];
|
|
715
|
-
const solProviders = masterConfig?.walletConfig?.chains.solana?.native ? (await getWalletProviders(core.Chain.Solana)).filter(nativeOnly) : [];
|
|
716
|
-
function attachEthereumListeners(provider, onWalletsChanged) {
|
|
717
|
-
if (typeof provider.on !== "function") return;
|
|
718
|
-
const handleAccountsChanged = accounts => {
|
|
719
|
-
if (accounts.length === 0) onWalletsChanged();
|
|
720
|
-
};
|
|
721
|
-
const handleDisconnect = () => onWalletsChanged();
|
|
722
|
-
provider.on("accountsChanged", handleAccountsChanged);
|
|
723
|
-
provider.on("disconnect", handleDisconnect);
|
|
724
|
-
return () => {
|
|
725
|
-
provider.removeListener("accountsChanged", handleAccountsChanged);
|
|
726
|
-
provider.removeListener("disconnect", handleDisconnect);
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
function attachSolanaListeners(provider, onWalletsChanged) {
|
|
730
|
-
const cleanups = [];
|
|
731
|
-
const walletEvents = provider.features?.["standard:events"];
|
|
732
|
-
if (walletEvents?.on) {
|
|
733
|
-
const handleChange = event => {
|
|
734
|
-
if (event.type === "change" || event.type === "accountsChanged") {
|
|
735
|
-
onWalletsChanged();
|
|
736
|
-
}
|
|
737
|
-
};
|
|
738
|
-
walletEvents.on("change", handleChange);
|
|
739
|
-
walletEvents.on("accountsChanged", handleChange);
|
|
740
|
-
cleanups.push(() => {
|
|
741
|
-
walletEvents.off?.("change", handleChange);
|
|
742
|
-
walletEvents.off?.("accountsChanged", handleChange);
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
return cleanups.length > 0 ? () => {
|
|
746
|
-
cleanups.forEach(fn => fn());
|
|
747
|
-
} : () => {};
|
|
748
|
-
}
|
|
749
|
-
ethProviders.forEach(p => {
|
|
750
|
-
const cleanup = attachEthereumListeners(p.provider, onWalletsChanged);
|
|
751
|
-
if (cleanup) cleanups.push(cleanup);
|
|
752
|
-
});
|
|
753
|
-
solProviders.forEach(p => {
|
|
754
|
-
const cleanup = attachSolanaListeners(p.provider, onWalletsChanged);
|
|
755
|
-
if (cleanup) cleanups.push(cleanup);
|
|
883
|
+
await client.switchWalletAccountChain({
|
|
884
|
+
...params,
|
|
885
|
+
walletProviders
|
|
756
886
|
});
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
async function loginWithWallet(params) {
|
|
887
|
+
}, [client, callbacks]);
|
|
888
|
+
const loginWithWallet = react.useCallback(async params => {
|
|
762
889
|
if (!client) {
|
|
763
890
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
764
891
|
}
|
|
@@ -768,11 +895,15 @@ const ClientProvider = ({
|
|
|
768
895
|
expirationSeconds
|
|
769
896
|
}), callbacks, "Failed to login with wallet");
|
|
770
897
|
if (res) {
|
|
771
|
-
await handlePostAuth(
|
|
898
|
+
await handlePostAuth({
|
|
899
|
+
method: base.AuthMethod.Wallet,
|
|
900
|
+
action: sdkTypes.AuthAction.LOGIN,
|
|
901
|
+
identifier: res.address
|
|
902
|
+
});
|
|
772
903
|
}
|
|
773
904
|
return res;
|
|
774
|
-
}
|
|
775
|
-
|
|
905
|
+
}, [client, callbacks]);
|
|
906
|
+
const signUpWithWallet = react.useCallback(async params => {
|
|
776
907
|
if (!client) {
|
|
777
908
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
778
909
|
}
|
|
@@ -793,11 +924,15 @@ const ClientProvider = ({
|
|
|
793
924
|
expirationSeconds
|
|
794
925
|
}), callbacks, "Failed to sign up with wallet");
|
|
795
926
|
if (res) {
|
|
796
|
-
await handlePostAuth(
|
|
927
|
+
await handlePostAuth({
|
|
928
|
+
method: base.AuthMethod.Wallet,
|
|
929
|
+
action: sdkTypes.AuthAction.SIGNUP,
|
|
930
|
+
identifier: res.address
|
|
931
|
+
});
|
|
797
932
|
}
|
|
798
933
|
return res;
|
|
799
|
-
}
|
|
800
|
-
|
|
934
|
+
}, [client, callbacks, masterConfig]);
|
|
935
|
+
const loginOrSignupWithWallet = react.useCallback(async params => {
|
|
801
936
|
if (!client) {
|
|
802
937
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
803
938
|
}
|
|
@@ -818,33 +953,41 @@ const ClientProvider = ({
|
|
|
818
953
|
expirationSeconds
|
|
819
954
|
}), callbacks, "Failed to login or sign up with wallet");
|
|
820
955
|
if (res) {
|
|
821
|
-
await handlePostAuth(
|
|
956
|
+
await handlePostAuth({
|
|
957
|
+
method: base.AuthMethod.Wallet,
|
|
958
|
+
action: res.action,
|
|
959
|
+
identifier: res.address
|
|
960
|
+
});
|
|
822
961
|
}
|
|
823
962
|
return res;
|
|
824
|
-
}
|
|
825
|
-
|
|
963
|
+
}, [client, callbacks, masterConfig]);
|
|
964
|
+
const initOtp = react.useCallback(async params => {
|
|
826
965
|
if (!client) {
|
|
827
966
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
828
967
|
}
|
|
829
968
|
return utils.withTurnkeyErrorHandling(() => client.initOtp(params), callbacks, "Failed to initialize OTP");
|
|
830
|
-
}
|
|
831
|
-
|
|
969
|
+
}, [client, callbacks]);
|
|
970
|
+
const verifyOtp = react.useCallback(async params => {
|
|
832
971
|
if (!client) {
|
|
833
972
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
834
973
|
}
|
|
835
974
|
return utils.withTurnkeyErrorHandling(() => client.verifyOtp(params), callbacks, "Failed to verify OTP");
|
|
836
|
-
}
|
|
837
|
-
|
|
975
|
+
}, [client, callbacks]);
|
|
976
|
+
const loginWithOtp = react.useCallback(async params => {
|
|
838
977
|
if (!client) {
|
|
839
978
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
840
979
|
}
|
|
841
980
|
const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOtp(params), callbacks, "Failed to login with OTP");
|
|
842
981
|
if (res) {
|
|
843
|
-
await handlePostAuth(
|
|
982
|
+
await handlePostAuth({
|
|
983
|
+
method: base.AuthMethod.Otp,
|
|
984
|
+
action: sdkTypes.AuthAction.LOGIN,
|
|
985
|
+
identifier: params.verificationToken
|
|
986
|
+
});
|
|
844
987
|
}
|
|
845
988
|
return res;
|
|
846
|
-
}
|
|
847
|
-
|
|
989
|
+
}, [client, callbacks]);
|
|
990
|
+
const signUpWithOtp = react.useCallback(async params => {
|
|
848
991
|
if (!client) {
|
|
849
992
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
850
993
|
}
|
|
@@ -868,11 +1011,15 @@ const ClientProvider = ({
|
|
|
868
1011
|
};
|
|
869
1012
|
const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOtp(params), callbacks, "Failed to sign up with OTP");
|
|
870
1013
|
if (res) {
|
|
871
|
-
await handlePostAuth(
|
|
1014
|
+
await handlePostAuth({
|
|
1015
|
+
method: base.AuthMethod.Otp,
|
|
1016
|
+
action: sdkTypes.AuthAction.SIGNUP,
|
|
1017
|
+
identifier: params.verificationToken
|
|
1018
|
+
});
|
|
872
1019
|
}
|
|
873
1020
|
return res;
|
|
874
|
-
}
|
|
875
|
-
|
|
1021
|
+
}, [client, callbacks, masterConfig]);
|
|
1022
|
+
const completeOtp = react.useCallback(async params => {
|
|
876
1023
|
if (!client) {
|
|
877
1024
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
878
1025
|
}
|
|
@@ -896,11 +1043,29 @@ const ClientProvider = ({
|
|
|
896
1043
|
};
|
|
897
1044
|
const res = await utils.withTurnkeyErrorHandling(() => client.completeOtp(params), callbacks, "Failed to complete OTP");
|
|
898
1045
|
if (res) {
|
|
899
|
-
await handlePostAuth(
|
|
1046
|
+
await handlePostAuth({
|
|
1047
|
+
method: base.AuthMethod.Otp,
|
|
1048
|
+
action: res.action,
|
|
1049
|
+
identifier: res.verificationToken
|
|
1050
|
+
});
|
|
900
1051
|
}
|
|
901
1052
|
return res;
|
|
902
|
-
}
|
|
903
|
-
|
|
1053
|
+
}, [client, callbacks, masterConfig]);
|
|
1054
|
+
const loginWithOauth = react.useCallback(async params => {
|
|
1055
|
+
if (!client) {
|
|
1056
|
+
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1057
|
+
}
|
|
1058
|
+
const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
|
|
1059
|
+
if (res) {
|
|
1060
|
+
await handlePostAuth({
|
|
1061
|
+
method: base.AuthMethod.Oauth,
|
|
1062
|
+
action: sdkTypes.AuthAction.LOGIN,
|
|
1063
|
+
identifier: params.oidcToken
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
return res;
|
|
1067
|
+
}, [client, callbacks]);
|
|
1068
|
+
const signUpWithOauth = react.useCallback(async params => {
|
|
904
1069
|
if (!client) {
|
|
905
1070
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
906
1071
|
}
|
|
@@ -908,30 +1073,24 @@ const ClientProvider = ({
|
|
|
908
1073
|
throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
909
1074
|
}
|
|
910
1075
|
// If createSubOrgParams is not provided, use the default from masterConfig
|
|
911
|
-
|
|
1076
|
+
let createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
|
|
912
1077
|
params = createSubOrgParams !== undefined ? {
|
|
913
1078
|
...params,
|
|
914
1079
|
createSubOrgParams
|
|
915
1080
|
} : {
|
|
916
1081
|
...params
|
|
917
1082
|
};
|
|
918
|
-
const res = await utils.withTurnkeyErrorHandling(() => client.
|
|
1083
|
+
const res = await utils.withTurnkeyErrorHandling(() => client.signUpWithOauth(params), callbacks, "Failed to sign up with OAuth");
|
|
919
1084
|
if (res) {
|
|
920
|
-
await handlePostAuth(
|
|
1085
|
+
await handlePostAuth({
|
|
1086
|
+
method: base.AuthMethod.Oauth,
|
|
1087
|
+
action: sdkTypes.AuthAction.SIGNUP,
|
|
1088
|
+
identifier: params.oidcToken
|
|
1089
|
+
});
|
|
921
1090
|
}
|
|
922
1091
|
return res;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
if (!client) {
|
|
926
|
-
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
927
|
-
}
|
|
928
|
-
const res = await utils.withTurnkeyErrorHandling(() => client.loginWithOauth(params), callbacks, "Failed to login with OAuth");
|
|
929
|
-
if (res) {
|
|
930
|
-
await handlePostAuth();
|
|
931
|
-
}
|
|
932
|
-
return res;
|
|
933
|
-
}
|
|
934
|
-
async function signUpWithOauth(params) {
|
|
1092
|
+
}, [client, callbacks, masterConfig]);
|
|
1093
|
+
const completeOauth = react.useCallback(async params => {
|
|
935
1094
|
if (!client) {
|
|
936
1095
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
937
1096
|
}
|
|
@@ -939,43 +1098,53 @@ const ClientProvider = ({
|
|
|
939
1098
|
throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
940
1099
|
}
|
|
941
1100
|
// If createSubOrgParams is not provided, use the default from masterConfig
|
|
942
|
-
|
|
1101
|
+
const createSubOrgParams = params.createSubOrgParams ?? masterConfig.auth?.createSuborgParams?.oauth;
|
|
943
1102
|
params = createSubOrgParams !== undefined ? {
|
|
944
1103
|
...params,
|
|
945
1104
|
createSubOrgParams
|
|
946
1105
|
} : {
|
|
947
1106
|
...params
|
|
948
1107
|
};
|
|
949
|
-
const res = await utils.withTurnkeyErrorHandling(() => client.
|
|
1108
|
+
const res = await utils.withTurnkeyErrorHandling(() => client.completeOauth(params), callbacks, "Failed to complete OAuth");
|
|
950
1109
|
if (res) {
|
|
951
|
-
await handlePostAuth(
|
|
1110
|
+
await handlePostAuth({
|
|
1111
|
+
method: base.AuthMethod.Oauth,
|
|
1112
|
+
action: res.action,
|
|
1113
|
+
identifier: params.oidcToken
|
|
1114
|
+
});
|
|
952
1115
|
}
|
|
953
1116
|
return res;
|
|
954
|
-
}
|
|
955
|
-
|
|
1117
|
+
}, [client, callbacks, masterConfig]);
|
|
1118
|
+
const fetchWallets = react.useCallback(async params => {
|
|
956
1119
|
if (!client) {
|
|
957
1120
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
958
1121
|
}
|
|
959
1122
|
return utils.withTurnkeyErrorHandling(() => client.fetchWallets(params), callbacks, "Failed to fetch wallets");
|
|
960
|
-
}
|
|
961
|
-
|
|
1123
|
+
}, [client, callbacks]);
|
|
1124
|
+
const fetchWalletAccounts = react.useCallback(async params => {
|
|
962
1125
|
if (!client) {
|
|
963
1126
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
964
1127
|
}
|
|
965
1128
|
return utils.withTurnkeyErrorHandling(() => client.fetchWalletAccounts(params), callbacks, "Failed to fetch wallet accounts");
|
|
966
|
-
}
|
|
967
|
-
|
|
1129
|
+
}, [client, callbacks]);
|
|
1130
|
+
const fetchPrivateKeys = react.useCallback(async params => {
|
|
1131
|
+
if (!client) {
|
|
1132
|
+
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1133
|
+
}
|
|
1134
|
+
return utils.withTurnkeyErrorHandling(() => client.fetchPrivateKeys(params), callbacks, "Failed to fetch private keys");
|
|
1135
|
+
}, [client, callbacks]);
|
|
1136
|
+
const signMessage = react.useCallback(async params => {
|
|
968
1137
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
969
1138
|
return utils.withTurnkeyErrorHandling(() => client.signMessage(params), callbacks, "Failed to sign message");
|
|
970
|
-
}
|
|
971
|
-
|
|
1139
|
+
}, [client, callbacks]);
|
|
1140
|
+
const handleSignMessage = react.useCallback(async params => {
|
|
972
1141
|
const {
|
|
973
1142
|
successPageDuration = 2000
|
|
974
1143
|
} = params;
|
|
975
1144
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
976
1145
|
return new Promise((resolve, reject) => {
|
|
977
1146
|
pushPage({
|
|
978
|
-
key: "Sign
|
|
1147
|
+
key: "Sign message",
|
|
979
1148
|
content: jsxRuntime.jsx(Message.SignMessageModal, {
|
|
980
1149
|
message: params.message,
|
|
981
1150
|
subText: params?.subText,
|
|
@@ -1000,146 +1169,146 @@ const ClientProvider = ({
|
|
|
1000
1169
|
})
|
|
1001
1170
|
});
|
|
1002
1171
|
});
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1172
|
+
}, [client, callbacks]);
|
|
1173
|
+
const signTransaction = react.useCallback(async params => {
|
|
1005
1174
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1006
1175
|
return utils.withTurnkeyErrorHandling(() => client.signTransaction(params), callbacks, "Failed to sign transaction");
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1176
|
+
}, [client, callbacks]);
|
|
1177
|
+
const signAndSendTransaction = react.useCallback(async params => {
|
|
1009
1178
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1010
1179
|
return utils.withTurnkeyErrorHandling(() => client.signAndSendTransaction(params), callbacks, "Failed to sign transaction");
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1180
|
+
}, [client, callbacks]);
|
|
1181
|
+
const fetchUser = react.useCallback(async params => {
|
|
1013
1182
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1014
1183
|
return utils.withTurnkeyErrorHandling(() => client.fetchUser(params), callbacks, "Failed to fetch user");
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1184
|
+
}, [client, callbacks]);
|
|
1185
|
+
const updateUserEmail = react.useCallback(async params => {
|
|
1017
1186
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1018
1187
|
const res = await utils.withTurnkeyErrorHandling(() => client.updateUserEmail(params), callbacks, "Failed to update user email");
|
|
1019
1188
|
if (res) await refreshUser({
|
|
1020
1189
|
stampWith: params?.stampWith
|
|
1021
1190
|
});
|
|
1022
1191
|
return res;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1192
|
+
}, [client, callbacks]);
|
|
1193
|
+
const removeUserEmail = react.useCallback(async params => {
|
|
1025
1194
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1026
1195
|
const res = await utils.withTurnkeyErrorHandling(() => client.removeUserEmail(params), callbacks, "Failed to remove user email");
|
|
1027
1196
|
if (res) await refreshUser({
|
|
1028
1197
|
stampWith: params?.stampWith
|
|
1029
1198
|
});
|
|
1030
1199
|
return res;
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1200
|
+
}, [client, callbacks]);
|
|
1201
|
+
const updateUserPhoneNumber = react.useCallback(async params => {
|
|
1033
1202
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1034
1203
|
const res = await utils.withTurnkeyErrorHandling(() => client.updateUserPhoneNumber(params), callbacks, "Failed to update user phone number");
|
|
1035
1204
|
if (res) await refreshUser({
|
|
1036
1205
|
stampWith: params?.stampWith
|
|
1037
1206
|
});
|
|
1038
1207
|
return res;
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1208
|
+
}, [client, callbacks]);
|
|
1209
|
+
const removeUserPhoneNumber = react.useCallback(async params => {
|
|
1041
1210
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1042
1211
|
const res = await utils.withTurnkeyErrorHandling(() => client.removeUserPhoneNumber(params), callbacks, "Failed to remove user phone number");
|
|
1043
1212
|
if (res) await refreshUser({
|
|
1044
1213
|
stampWith: params?.stampWith
|
|
1045
1214
|
});
|
|
1046
1215
|
return res;
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1216
|
+
}, [client, callbacks]);
|
|
1217
|
+
const updateUserName = react.useCallback(async params => {
|
|
1049
1218
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1050
1219
|
const res = await utils.withTurnkeyErrorHandling(() => client.updateUserName(params), callbacks, "Failed to update user name");
|
|
1051
1220
|
if (res) await refreshUser({
|
|
1052
1221
|
stampWith: params?.stampWith
|
|
1053
1222
|
});
|
|
1054
1223
|
return res;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1224
|
+
}, [client, callbacks]);
|
|
1225
|
+
const addOauthProvider = react.useCallback(async params => {
|
|
1057
1226
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1058
1227
|
const res = await utils.withTurnkeyErrorHandling(() => client.addOauthProvider(params), callbacks, "Failed to add OAuth provider");
|
|
1059
1228
|
if (res) await refreshUser({
|
|
1060
1229
|
stampWith: params?.stampWith
|
|
1061
1230
|
});
|
|
1062
1231
|
return res;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1232
|
+
}, [client, callbacks]);
|
|
1233
|
+
const removeOauthProviders = react.useCallback(async params => {
|
|
1065
1234
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1066
1235
|
const res = await utils.withTurnkeyErrorHandling(() => client.removeOauthProviders(params), callbacks, "Failed to remove OAuth providers");
|
|
1067
1236
|
if (res) await refreshUser({
|
|
1068
1237
|
stampWith: params?.stampWith
|
|
1069
1238
|
});
|
|
1070
1239
|
return res;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1240
|
+
}, [client, callbacks]);
|
|
1241
|
+
const addPasskey = react.useCallback(async params => {
|
|
1073
1242
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1074
1243
|
const res = await utils.withTurnkeyErrorHandling(() => client.addPasskey(params), callbacks, "Failed to add passkey");
|
|
1075
1244
|
if (res) await refreshUser({
|
|
1076
1245
|
stampWith: params?.stampWith
|
|
1077
1246
|
});
|
|
1078
1247
|
return res;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1248
|
+
}, [client, callbacks]);
|
|
1249
|
+
const removePasskeys = react.useCallback(async params => {
|
|
1081
1250
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1082
1251
|
const res = await utils.withTurnkeyErrorHandling(() => client.removePasskeys(params), callbacks, "Failed to remove passkeys");
|
|
1083
1252
|
if (res) await refreshUser({
|
|
1084
1253
|
stampWith: params?.stampWith
|
|
1085
1254
|
});
|
|
1086
1255
|
return res;
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1256
|
+
}, [client, callbacks]);
|
|
1257
|
+
const createWallet = react.useCallback(async params => {
|
|
1089
1258
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1090
1259
|
const res = await utils.withTurnkeyErrorHandling(() => client.createWallet(params), callbacks, "Failed to create wallet");
|
|
1091
1260
|
if (res) await refreshWallets({
|
|
1092
1261
|
stampWith: params?.stampWith
|
|
1093
1262
|
});
|
|
1094
1263
|
return res;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1264
|
+
}, [client, callbacks]);
|
|
1265
|
+
const createWalletAccounts = react.useCallback(async params => {
|
|
1097
1266
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1098
1267
|
const res = await utils.withTurnkeyErrorHandling(() => client.createWalletAccounts(params), callbacks, "Failed to create wallet accounts");
|
|
1099
1268
|
if (res) await refreshWallets({
|
|
1100
1269
|
stampWith: params?.stampWith
|
|
1101
1270
|
});
|
|
1102
1271
|
return res;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1272
|
+
}, [client, callbacks]);
|
|
1273
|
+
const exportWallet = react.useCallback(async params => {
|
|
1105
1274
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1106
1275
|
const res = await utils.withTurnkeyErrorHandling(() => client.exportWallet(params), callbacks, "Failed to export wallet");
|
|
1107
1276
|
if (res) await refreshWallets({
|
|
1108
1277
|
stampWith: params?.stampWith
|
|
1109
1278
|
});
|
|
1110
1279
|
return res;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1280
|
+
}, [client, callbacks]);
|
|
1281
|
+
const exportPrivateKey = react.useCallback(async params => {
|
|
1113
1282
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1114
1283
|
const res = await utils.withTurnkeyErrorHandling(() => client.exportPrivateKey(params), callbacks, "Failed to export private key");
|
|
1115
1284
|
return res;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1285
|
+
}, [client, callbacks]);
|
|
1286
|
+
const exportWalletAccount = react.useCallback(async params => {
|
|
1118
1287
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1119
1288
|
const res = await utils.withTurnkeyErrorHandling(() => client.exportWalletAccount(params), callbacks, "Failed to export wallet accounts");
|
|
1120
1289
|
if (res) await refreshWallets({
|
|
1121
1290
|
stampWith: params?.stampWith
|
|
1122
1291
|
});
|
|
1123
1292
|
return res;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1293
|
+
}, [client, callbacks]);
|
|
1294
|
+
const importWallet = react.useCallback(async params => {
|
|
1126
1295
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1127
1296
|
const res = await utils.withTurnkeyErrorHandling(() => client.importWallet(params), callbacks, "Failed to import wallet");
|
|
1128
1297
|
if (res) await refreshWallets({
|
|
1129
1298
|
stampWith: params?.stampWith
|
|
1130
1299
|
});
|
|
1131
1300
|
return res;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1301
|
+
}, [client, callbacks]);
|
|
1302
|
+
const importPrivateKey = react.useCallback(async params => {
|
|
1134
1303
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1135
1304
|
const res = await utils.withTurnkeyErrorHandling(() => client.importPrivateKey(params), callbacks, "Failed to import private key");
|
|
1136
1305
|
return res;
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1306
|
+
}, [client, callbacks]);
|
|
1307
|
+
const deleteSubOrganization = react.useCallback(async params => {
|
|
1139
1308
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1140
1309
|
return utils.withTurnkeyErrorHandling(() => client.deleteSubOrganization(params), callbacks, "Failed to delete sub-organization");
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1310
|
+
}, [client, callbacks]);
|
|
1311
|
+
const storeSession = react.useCallback(async params => {
|
|
1143
1312
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1144
1313
|
await utils.withTurnkeyErrorHandling(() => client.storeSession(params), callbacks, "Failed to store session");
|
|
1145
1314
|
const sessionKey = await getActiveSessionKey();
|
|
@@ -1156,8 +1325,8 @@ const ClientProvider = ({
|
|
|
1156
1325
|
setSession(session);
|
|
1157
1326
|
setAllSessions(allSessions);
|
|
1158
1327
|
return;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1328
|
+
}, [client, callbacks]);
|
|
1329
|
+
const clearSession = react.useCallback(async params => {
|
|
1161
1330
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1162
1331
|
await utils.withTurnkeyErrorHandling(() => client.clearSession(params), callbacks, "Failed to clear session");
|
|
1163
1332
|
const session = await getSession();
|
|
@@ -1165,14 +1334,14 @@ const ClientProvider = ({
|
|
|
1165
1334
|
setSession(session);
|
|
1166
1335
|
setAllSessions(allSessions);
|
|
1167
1336
|
return;
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1337
|
+
}, [client, callbacks]);
|
|
1338
|
+
const clearAllSessions = react.useCallback(async () => {
|
|
1170
1339
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1171
1340
|
setSession(undefined);
|
|
1172
1341
|
setAllSessions(undefined);
|
|
1173
1342
|
return await utils.withTurnkeyErrorHandling(() => client.clearAllSessions(), callbacks, "Failed to clear all sessions");
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1343
|
+
}, [client, callbacks]);
|
|
1344
|
+
const refreshSession = react.useCallback(async params => {
|
|
1176
1345
|
if (!client) {
|
|
1177
1346
|
throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1178
1347
|
}
|
|
@@ -1200,16 +1369,16 @@ const ClientProvider = ({
|
|
|
1200
1369
|
setSession(session);
|
|
1201
1370
|
setAllSessions(allSessions);
|
|
1202
1371
|
return res;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1372
|
+
}, [client, callbacks]);
|
|
1373
|
+
const getSession = react.useCallback(async params => {
|
|
1205
1374
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1206
1375
|
return utils.withTurnkeyErrorHandling(() => client.getSession(params), callbacks, "Failed to get session");
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1376
|
+
}, [client, callbacks]);
|
|
1377
|
+
const getAllSessions = react.useCallback(async () => {
|
|
1209
1378
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1210
1379
|
return utils.withTurnkeyErrorHandling(() => client.getAllSessions(), callbacks, "Failed to get all sessions");
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1380
|
+
}, [client, callbacks]);
|
|
1381
|
+
const setActiveSession = react.useCallback(async params => {
|
|
1213
1382
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1214
1383
|
const session = await utils.withTurnkeyErrorHandling(() => client.getSession({
|
|
1215
1384
|
sessionKey: params.sessionKey
|
|
@@ -1219,25 +1388,29 @@ const ClientProvider = ({
|
|
|
1219
1388
|
}
|
|
1220
1389
|
await utils.withTurnkeyErrorHandling(() => client.setActiveSession(params), callbacks, "Failed to set active session");
|
|
1221
1390
|
setSession(session);
|
|
1391
|
+
await utils.withTurnkeyErrorHandling(async () => {
|
|
1392
|
+
await refreshWallets();
|
|
1393
|
+
await refreshUser();
|
|
1394
|
+
}, callbacks, "Failed to refresh data after setting active session");
|
|
1222
1395
|
return;
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1396
|
+
}, [client, callbacks]);
|
|
1397
|
+
const getActiveSessionKey = react.useCallback(async () => {
|
|
1225
1398
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1226
1399
|
return utils.withTurnkeyErrorHandling(() => client.getActiveSessionKey(), callbacks, "Failed to get active session key");
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1400
|
+
}, [client, callbacks]);
|
|
1401
|
+
const clearUnusedKeyPairs = react.useCallback(async () => {
|
|
1229
1402
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1230
1403
|
return utils.withTurnkeyErrorHandling(() => client.clearUnusedKeyPairs(), callbacks, "Failed to clear unused key pairs");
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1404
|
+
}, [client, callbacks]);
|
|
1405
|
+
const createApiKeyPair = react.useCallback(async params => {
|
|
1233
1406
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1234
1407
|
return utils.withTurnkeyErrorHandling(() => client.createApiKeyPair(params), callbacks, "Failed to create API key pair");
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1408
|
+
}, [client, callbacks]);
|
|
1409
|
+
const getProxyAuthConfig = react.useCallback(async () => {
|
|
1237
1410
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1238
1411
|
return utils.withTurnkeyErrorHandling(() => client.getProxyAuthConfig(), callbacks, "Failed to get proxy auth config");
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1412
|
+
}, [client, callbacks]);
|
|
1413
|
+
const refreshUser = react.useCallback(async params => {
|
|
1241
1414
|
const {
|
|
1242
1415
|
stampWith
|
|
1243
1416
|
} = params || {};
|
|
@@ -1248,20 +1421,298 @@ const ClientProvider = ({
|
|
|
1248
1421
|
if (user) {
|
|
1249
1422
|
setUser(user);
|
|
1250
1423
|
}
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1424
|
+
}, [client, callbacks]);
|
|
1425
|
+
const refreshWallets = react.useCallback(async params => {
|
|
1426
|
+
const {
|
|
1427
|
+
stampWith
|
|
1428
|
+
} = params || {};
|
|
1429
|
+
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
1430
|
+
const walletProviders = await utils.withTurnkeyErrorHandling(() => getWalletProviders(), callbacks, "Failed to refresh wallets");
|
|
1431
|
+
const wallets = await utils.withTurnkeyErrorHandling(() => fetchWallets({
|
|
1432
|
+
stampWith,
|
|
1433
|
+
walletProviders
|
|
1434
|
+
}), callbacks, "Failed to refresh wallets");
|
|
1435
|
+
if (wallets) {
|
|
1436
|
+
setWallets(wallets);
|
|
1437
|
+
}
|
|
1438
|
+
}, [client, callbacks, getWalletProviders, fetchWallets]);
|
|
1439
|
+
const handleDiscordOauth = react.useCallback(async params => {
|
|
1440
|
+
const {
|
|
1441
|
+
clientId = masterConfig?.auth?.oauthConfig?.discordClientId,
|
|
1442
|
+
openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
|
|
1443
|
+
additionalState: additionalParameters
|
|
1444
|
+
} = params || {};
|
|
1445
|
+
try {
|
|
1446
|
+
if (!masterConfig) {
|
|
1447
|
+
throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
1448
|
+
}
|
|
1449
|
+
if (!clientId) {
|
|
1450
|
+
throw new sdkTypes.TurnkeyError("Discord Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
1451
|
+
}
|
|
1452
|
+
if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
|
|
1453
|
+
throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
1454
|
+
}
|
|
1455
|
+
const flow = openInPage ? "redirect" : "popup";
|
|
1456
|
+
const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
|
|
1457
|
+
// Create key pair and generate nonce
|
|
1458
|
+
const publicKey = await createApiKeyPair();
|
|
1459
|
+
if (!publicKey) {
|
|
1460
|
+
throw new Error("Failed to create public key for OAuth.");
|
|
1461
|
+
}
|
|
1462
|
+
const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
|
|
1463
|
+
// Generate PKCE challenge pair
|
|
1464
|
+
const {
|
|
1465
|
+
verifier,
|
|
1466
|
+
codeChallenge
|
|
1467
|
+
} = await utils.generateChallengePair();
|
|
1468
|
+
sessionStorage.setItem("discord_verifier", verifier);
|
|
1469
|
+
// Construct Discord Auth URL
|
|
1470
|
+
const discordAuthUrl = new URL(utils.DISCORD_AUTH_URL);
|
|
1471
|
+
discordAuthUrl.searchParams.set("client_id", clientId);
|
|
1472
|
+
discordAuthUrl.searchParams.set("redirect_uri", redirectURI);
|
|
1473
|
+
discordAuthUrl.searchParams.set("response_type", "code");
|
|
1474
|
+
discordAuthUrl.searchParams.set("code_challenge", codeChallenge);
|
|
1475
|
+
discordAuthUrl.searchParams.set("code_challenge_method", "S256");
|
|
1476
|
+
discordAuthUrl.searchParams.set("scope", "identify email");
|
|
1477
|
+
discordAuthUrl.searchParams.set("state", `provider=discord&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
|
|
1478
|
+
if (additionalParameters) {
|
|
1479
|
+
const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
1480
|
+
if (extra) {
|
|
1481
|
+
discordAuthUrl.searchParams.set("state", discordAuthUrl.searchParams.get("state") + `&${extra}`);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
if (openInPage) {
|
|
1485
|
+
window.location.href = discordAuthUrl.toString();
|
|
1486
|
+
return new Promise((_, reject) => {
|
|
1487
|
+
const timeout = setTimeout(() => {
|
|
1488
|
+
reject(new Error("Authentication timed out."));
|
|
1489
|
+
}, 300000);
|
|
1490
|
+
window.addEventListener("beforeunload", () => clearTimeout(timeout));
|
|
1491
|
+
});
|
|
1492
|
+
} else {
|
|
1493
|
+
const width = utils.popupWidth;
|
|
1494
|
+
const height = utils.popupHeight;
|
|
1495
|
+
const left = window.screenX + (window.innerWidth - width) / 2;
|
|
1496
|
+
const top = window.screenY + (window.innerHeight - height) / 2;
|
|
1497
|
+
const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
|
|
1498
|
+
if (!authWindow) {
|
|
1499
|
+
throw new Error("Failed to open Discord login window.");
|
|
1500
|
+
}
|
|
1501
|
+
authWindow.location.href = discordAuthUrl.toString();
|
|
1502
|
+
return new Promise((resolve, reject) => {
|
|
1503
|
+
const interval = setInterval(() => {
|
|
1504
|
+
try {
|
|
1505
|
+
if (authWindow.closed) {
|
|
1506
|
+
clearInterval(interval);
|
|
1507
|
+
reject(new Error("Authentication window was closed."));
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
const url = authWindow.location.href || "";
|
|
1511
|
+
if (url.startsWith(window.location.origin)) {
|
|
1512
|
+
const urlParams = new URLSearchParams(new URL(url).search);
|
|
1513
|
+
const authCode = urlParams.get("code");
|
|
1514
|
+
const stateParam = urlParams.get("state");
|
|
1515
|
+
const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
|
|
1516
|
+
if (authCode && stateParam && stateParam.includes("provider=discord")) {
|
|
1517
|
+
authWindow.close();
|
|
1518
|
+
clearInterval(interval);
|
|
1519
|
+
const verifier = sessionStorage.getItem("discord_verifier");
|
|
1520
|
+
if (!verifier) {
|
|
1521
|
+
reject(new Error("Missing PKCE verifier"));
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
client?.httpClient.proxyOAuth2Authenticate({
|
|
1525
|
+
provider: "OAUTH2_PROVIDER_DISCORD",
|
|
1526
|
+
authCode,
|
|
1527
|
+
redirectUri: redirectURI,
|
|
1528
|
+
codeVerifier: verifier,
|
|
1529
|
+
nonce: nonce
|
|
1530
|
+
}).then(resp => {
|
|
1531
|
+
sessionStorage.removeItem("discord_verifier");
|
|
1532
|
+
const oidcToken = resp.oidcToken;
|
|
1533
|
+
if (params?.onOauthSuccess) {
|
|
1534
|
+
params.onOauthSuccess({
|
|
1535
|
+
oidcToken,
|
|
1536
|
+
providerName: "discord",
|
|
1537
|
+
...(sessionKey && {
|
|
1538
|
+
sessionKey
|
|
1539
|
+
})
|
|
1540
|
+
});
|
|
1541
|
+
} else if (callbacks?.onOauthRedirect) {
|
|
1542
|
+
callbacks.onOauthRedirect({
|
|
1543
|
+
idToken: oidcToken,
|
|
1544
|
+
publicKey,
|
|
1545
|
+
...(sessionKey && {
|
|
1546
|
+
sessionKey
|
|
1547
|
+
})
|
|
1548
|
+
});
|
|
1549
|
+
} else {
|
|
1550
|
+
completeOauth({
|
|
1551
|
+
oidcToken,
|
|
1552
|
+
publicKey,
|
|
1553
|
+
providerName: "discord",
|
|
1554
|
+
...(sessionKey && {
|
|
1555
|
+
sessionKey
|
|
1556
|
+
})
|
|
1557
|
+
}).then(() => resolve()).catch(reject);
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
resolve();
|
|
1561
|
+
}).catch(reject);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
} catch {
|
|
1565
|
+
// ignore cross-origin
|
|
1566
|
+
}
|
|
1567
|
+
}, 500);
|
|
1568
|
+
if (authWindow.closed) {
|
|
1569
|
+
clearInterval(interval);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
throw error;
|
|
1575
|
+
}
|
|
1576
|
+
}, [client, callbacks]);
|
|
1577
|
+
const handleXOauth = react.useCallback(async params => {
|
|
1253
1578
|
const {
|
|
1254
|
-
|
|
1579
|
+
clientId = masterConfig?.auth?.oauthConfig?.xClientId,
|
|
1580
|
+
openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
|
|
1581
|
+
additionalState: additionalParameters
|
|
1255
1582
|
} = params || {};
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1583
|
+
try {
|
|
1584
|
+
if (!masterConfig) {
|
|
1585
|
+
throw new sdkTypes.TurnkeyError("Config is not ready yet!", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
1586
|
+
}
|
|
1587
|
+
if (!clientId) {
|
|
1588
|
+
throw new sdkTypes.TurnkeyError("Twitter Client ID is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
1589
|
+
}
|
|
1590
|
+
if (!masterConfig.auth?.oauthConfig?.oauthRedirectUri) {
|
|
1591
|
+
throw new sdkTypes.TurnkeyError("OAuth Redirect URI is not configured.", sdkTypes.TurnkeyErrorCodes.INVALID_CONFIGURATION);
|
|
1592
|
+
}
|
|
1593
|
+
const flow = openInPage ? "redirect" : "popup";
|
|
1594
|
+
const redirectURI = masterConfig.auth?.oauthConfig.oauthRedirectUri;
|
|
1595
|
+
// Create key pair and generate nonce
|
|
1596
|
+
const publicKey = await createApiKeyPair();
|
|
1597
|
+
if (!publicKey) {
|
|
1598
|
+
throw new Error("Failed to create public key for OAuth.");
|
|
1599
|
+
}
|
|
1600
|
+
const nonce = utils$1.bytesToHex(sha2.sha256(publicKey));
|
|
1601
|
+
// Generate PKCE challenge pair
|
|
1602
|
+
const {
|
|
1603
|
+
verifier,
|
|
1604
|
+
codeChallenge
|
|
1605
|
+
} = await utils.generateChallengePair();
|
|
1606
|
+
sessionStorage.setItem("twitter_verifier", verifier);
|
|
1607
|
+
// Construct Twitter Auth URL
|
|
1608
|
+
const twitterAuthUrl = new URL(utils.X_AUTH_URL);
|
|
1609
|
+
twitterAuthUrl.searchParams.set("client_id", clientId);
|
|
1610
|
+
twitterAuthUrl.searchParams.set("redirect_uri", redirectURI);
|
|
1611
|
+
twitterAuthUrl.searchParams.set("response_type", "code");
|
|
1612
|
+
twitterAuthUrl.searchParams.set("code_challenge", codeChallenge);
|
|
1613
|
+
twitterAuthUrl.searchParams.set("code_challenge_method", "S256");
|
|
1614
|
+
twitterAuthUrl.searchParams.set("scope", "tweet.read users.read");
|
|
1615
|
+
twitterAuthUrl.searchParams.set("state", `provider=twitter&flow=${flow}&publicKey=${encodeURIComponent(publicKey)}&nonce=${nonce}`);
|
|
1616
|
+
if (additionalParameters) {
|
|
1617
|
+
const extra = Object.entries(additionalParameters).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
1618
|
+
if (extra) {
|
|
1619
|
+
twitterAuthUrl.searchParams.set("state", twitterAuthUrl.searchParams.get("state") + `&${extra}`);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (openInPage) {
|
|
1623
|
+
window.location.href = twitterAuthUrl.toString();
|
|
1624
|
+
return new Promise((_, reject) => {
|
|
1625
|
+
const timeout = setTimeout(() => {
|
|
1626
|
+
reject(new Error("Authentication timed out."));
|
|
1627
|
+
}, 300000);
|
|
1628
|
+
window.addEventListener("beforeunload", () => clearTimeout(timeout));
|
|
1629
|
+
});
|
|
1630
|
+
} else {
|
|
1631
|
+
const width = utils.popupWidth;
|
|
1632
|
+
const height = utils.popupHeight;
|
|
1633
|
+
const left = window.screenX + (window.innerWidth - width) / 2;
|
|
1634
|
+
const top = window.screenY + (window.innerHeight - height) / 2;
|
|
1635
|
+
const authWindow = window.open("about:blank", "_blank", `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
|
|
1636
|
+
if (!authWindow) {
|
|
1637
|
+
throw new Error("Failed to open Twitter login window.");
|
|
1638
|
+
}
|
|
1639
|
+
authWindow.location.href = twitterAuthUrl.toString();
|
|
1640
|
+
return new Promise((resolve, reject) => {
|
|
1641
|
+
const interval = setInterval(() => {
|
|
1642
|
+
try {
|
|
1643
|
+
if (authWindow.closed) {
|
|
1644
|
+
clearInterval(interval);
|
|
1645
|
+
reject(new Error("Authentication window was closed."));
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
const url = authWindow.location.href || "";
|
|
1649
|
+
if (url.startsWith(window.location.origin)) {
|
|
1650
|
+
const urlParams = new URLSearchParams(new URL(url).search);
|
|
1651
|
+
const authCode = urlParams.get("code");
|
|
1652
|
+
const stateParam = urlParams.get("state");
|
|
1653
|
+
const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
|
|
1654
|
+
if (authCode && stateParam && stateParam.includes("provider=twitter")) {
|
|
1655
|
+
authWindow.close();
|
|
1656
|
+
clearInterval(interval);
|
|
1657
|
+
const verifier = sessionStorage.getItem("twitter_verifier");
|
|
1658
|
+
if (!verifier) {
|
|
1659
|
+
reject(new Error("Missing PKCE verifier"));
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
client?.httpClient.proxyOAuth2Authenticate({
|
|
1663
|
+
provider: "OAUTH2_PROVIDER_X",
|
|
1664
|
+
authCode,
|
|
1665
|
+
redirectUri: redirectURI,
|
|
1666
|
+
codeVerifier: verifier,
|
|
1667
|
+
nonce
|
|
1668
|
+
}).then(resp => {
|
|
1669
|
+
sessionStorage.removeItem("twitter_verifier");
|
|
1670
|
+
const oidcToken = resp.oidcToken;
|
|
1671
|
+
if (params?.onOauthSuccess) {
|
|
1672
|
+
params.onOauthSuccess({
|
|
1673
|
+
oidcToken,
|
|
1674
|
+
providerName: "twitter",
|
|
1675
|
+
...(sessionKey && {
|
|
1676
|
+
sessionKey
|
|
1677
|
+
})
|
|
1678
|
+
});
|
|
1679
|
+
} else if (callbacks?.onOauthRedirect) {
|
|
1680
|
+
callbacks.onOauthRedirect({
|
|
1681
|
+
idToken: oidcToken,
|
|
1682
|
+
publicKey,
|
|
1683
|
+
...(sessionKey && {
|
|
1684
|
+
sessionKey
|
|
1685
|
+
})
|
|
1686
|
+
});
|
|
1687
|
+
} else {
|
|
1688
|
+
completeOauth({
|
|
1689
|
+
oidcToken,
|
|
1690
|
+
publicKey,
|
|
1691
|
+
providerName: "twitter",
|
|
1692
|
+
...(sessionKey && {
|
|
1693
|
+
sessionKey
|
|
1694
|
+
})
|
|
1695
|
+
}).then(() => resolve()).catch(reject);
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
resolve();
|
|
1699
|
+
}).catch(reject);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
} catch {
|
|
1703
|
+
// ignore cross-origin
|
|
1704
|
+
}
|
|
1705
|
+
}, 500);
|
|
1706
|
+
if (authWindow.closed) {
|
|
1707
|
+
clearInterval(interval);
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
} catch (error) {
|
|
1712
|
+
throw error;
|
|
1262
1713
|
}
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1714
|
+
}, [client, callbacks]);
|
|
1715
|
+
const handleGoogleOauth = react.useCallback(async params => {
|
|
1265
1716
|
const {
|
|
1266
1717
|
clientId = masterConfig?.auth?.oauthConfig?.googleClientId,
|
|
1267
1718
|
openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
|
|
@@ -1341,24 +1792,35 @@ const ClientProvider = ({
|
|
|
1341
1792
|
if (url.startsWith(window.location.origin)) {
|
|
1342
1793
|
const hashParams = new URLSearchParams(url.split("#")[1]);
|
|
1343
1794
|
const idToken = hashParams.get("id_token");
|
|
1795
|
+
const stateParams = hashParams.get("state");
|
|
1796
|
+
const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
|
|
1344
1797
|
if (idToken) {
|
|
1345
1798
|
authWindow.close();
|
|
1346
1799
|
clearInterval(interval);
|
|
1347
1800
|
if (params?.onOauthSuccess) {
|
|
1348
1801
|
params.onOauthSuccess({
|
|
1349
1802
|
oidcToken: idToken,
|
|
1350
|
-
providerName: "google"
|
|
1803
|
+
providerName: "google",
|
|
1804
|
+
...(sessionKey && {
|
|
1805
|
+
sessionKey
|
|
1806
|
+
})
|
|
1351
1807
|
});
|
|
1352
1808
|
} else if (callbacks?.onOauthRedirect) {
|
|
1353
1809
|
callbacks.onOauthRedirect({
|
|
1354
1810
|
idToken,
|
|
1355
|
-
publicKey
|
|
1811
|
+
publicKey,
|
|
1812
|
+
...(sessionKey && {
|
|
1813
|
+
sessionKey
|
|
1814
|
+
})
|
|
1356
1815
|
});
|
|
1357
1816
|
} else {
|
|
1358
1817
|
completeOauth({
|
|
1359
1818
|
oidcToken: idToken,
|
|
1360
1819
|
publicKey,
|
|
1361
|
-
providerName: "google"
|
|
1820
|
+
providerName: "google",
|
|
1821
|
+
...(sessionKey && {
|
|
1822
|
+
sessionKey
|
|
1823
|
+
})
|
|
1362
1824
|
}).then(() => resolve()).catch(reject);
|
|
1363
1825
|
return;
|
|
1364
1826
|
}
|
|
@@ -1377,8 +1839,8 @@ const ClientProvider = ({
|
|
|
1377
1839
|
} catch (error) {
|
|
1378
1840
|
throw error;
|
|
1379
1841
|
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1842
|
+
}, [client, callbacks]);
|
|
1843
|
+
const handleAppleOauth = react.useCallback(async params => {
|
|
1382
1844
|
const {
|
|
1383
1845
|
clientId = masterConfig?.auth?.oauthConfig?.appleClientId,
|
|
1384
1846
|
openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
|
|
@@ -1454,24 +1916,35 @@ const ClientProvider = ({
|
|
|
1454
1916
|
if (url.startsWith(window.location.origin)) {
|
|
1455
1917
|
const hashParams = new URLSearchParams(url.split("#")[1]);
|
|
1456
1918
|
const idToken = hashParams.get("id_token");
|
|
1919
|
+
const stateParams = hashParams.get("state");
|
|
1920
|
+
const sessionKey = stateParams?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
|
|
1457
1921
|
if (idToken) {
|
|
1458
1922
|
authWindow.close();
|
|
1459
1923
|
clearInterval(interval);
|
|
1460
1924
|
if (params?.onOauthSuccess) {
|
|
1461
1925
|
params.onOauthSuccess({
|
|
1462
1926
|
oidcToken: idToken,
|
|
1463
|
-
providerName: "apple"
|
|
1927
|
+
providerName: "apple",
|
|
1928
|
+
...(sessionKey && {
|
|
1929
|
+
sessionKey
|
|
1930
|
+
})
|
|
1464
1931
|
});
|
|
1465
1932
|
} else if (callbacks?.onOauthRedirect) {
|
|
1466
1933
|
callbacks.onOauthRedirect({
|
|
1467
1934
|
idToken,
|
|
1468
|
-
publicKey
|
|
1935
|
+
publicKey,
|
|
1936
|
+
...(sessionKey && {
|
|
1937
|
+
sessionKey
|
|
1938
|
+
})
|
|
1469
1939
|
});
|
|
1470
1940
|
} else {
|
|
1471
1941
|
completeOauth({
|
|
1472
1942
|
oidcToken: idToken,
|
|
1473
1943
|
publicKey,
|
|
1474
|
-
providerName: "apple"
|
|
1944
|
+
providerName: "apple",
|
|
1945
|
+
...(sessionKey && {
|
|
1946
|
+
sessionKey
|
|
1947
|
+
})
|
|
1475
1948
|
}).then(() => resolve()).catch(reject);
|
|
1476
1949
|
return;
|
|
1477
1950
|
}
|
|
@@ -1490,8 +1963,8 @@ const ClientProvider = ({
|
|
|
1490
1963
|
} catch (error) {
|
|
1491
1964
|
throw error;
|
|
1492
1965
|
}
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1966
|
+
}, [client, callbacks]);
|
|
1967
|
+
const handleFacebookOauth = react.useCallback(async params => {
|
|
1495
1968
|
const {
|
|
1496
1969
|
clientId = masterConfig?.auth?.oauthConfig?.facebookClientId,
|
|
1497
1970
|
openInPage = masterConfig?.auth?.oauthConfig?.openOauthInPage ?? false,
|
|
@@ -1578,6 +2051,7 @@ const ClientProvider = ({
|
|
|
1578
2051
|
const authCode = urlParams.get("code");
|
|
1579
2052
|
const stateParam = urlParams.get("state");
|
|
1580
2053
|
if (authCode && stateParam && stateParam.includes("provider=facebook")) {
|
|
2054
|
+
const sessionKey = stateParam?.split("&").find(param => param.startsWith("sessionKey="))?.split("=")[1];
|
|
1581
2055
|
authWindow.close();
|
|
1582
2056
|
clearInterval(interval);
|
|
1583
2057
|
// Exchange code for token
|
|
@@ -1591,18 +2065,27 @@ const ClientProvider = ({
|
|
|
1591
2065
|
if (params?.onOauthSuccess) {
|
|
1592
2066
|
params.onOauthSuccess({
|
|
1593
2067
|
oidcToken: tokenData.id_token,
|
|
1594
|
-
providerName: "
|
|
2068
|
+
providerName: "facebook",
|
|
2069
|
+
...(sessionKey && {
|
|
2070
|
+
sessionKey
|
|
2071
|
+
})
|
|
1595
2072
|
});
|
|
1596
2073
|
} else if (callbacks?.onOauthRedirect) {
|
|
1597
2074
|
callbacks.onOauthRedirect({
|
|
1598
2075
|
idToken: tokenData.id_token,
|
|
1599
|
-
publicKey
|
|
2076
|
+
publicKey,
|
|
2077
|
+
...(sessionKey && {
|
|
2078
|
+
sessionKey
|
|
2079
|
+
})
|
|
1600
2080
|
});
|
|
1601
2081
|
} else {
|
|
1602
2082
|
completeOauth({
|
|
1603
2083
|
oidcToken: tokenData.id_token,
|
|
1604
2084
|
publicKey,
|
|
1605
|
-
providerName: "facebook"
|
|
2085
|
+
providerName: "facebook",
|
|
2086
|
+
...(sessionKey && {
|
|
2087
|
+
sessionKey
|
|
2088
|
+
})
|
|
1606
2089
|
}).then(() => resolve()).catch(reject);
|
|
1607
2090
|
return;
|
|
1608
2091
|
}
|
|
@@ -1622,21 +2105,23 @@ const ClientProvider = ({
|
|
|
1622
2105
|
} catch (error) {
|
|
1623
2106
|
throw error;
|
|
1624
2107
|
}
|
|
1625
|
-
}
|
|
1626
|
-
const handleLogin = async
|
|
2108
|
+
}, [client, callbacks]);
|
|
2109
|
+
const handleLogin = react.useCallback(async params => {
|
|
1627
2110
|
pushPage({
|
|
1628
2111
|
key: "Log in or sign up",
|
|
1629
|
-
content: jsxRuntime.jsx(index.AuthComponent, {
|
|
2112
|
+
content: jsxRuntime.jsx(index.AuthComponent, {
|
|
2113
|
+
sessionKey: params?.sessionKey
|
|
2114
|
+
})
|
|
1630
2115
|
});
|
|
1631
|
-
};
|
|
1632
|
-
const handleExportWallet = async params => {
|
|
2116
|
+
}, [pushPage]);
|
|
2117
|
+
const handleExportWallet = react.useCallback(async params => {
|
|
1633
2118
|
const {
|
|
1634
2119
|
walletId,
|
|
1635
2120
|
targetPublicKey,
|
|
1636
2121
|
stampWith
|
|
1637
2122
|
} = params;
|
|
1638
2123
|
pushPage({
|
|
1639
|
-
key: "Export
|
|
2124
|
+
key: "Export wallet",
|
|
1640
2125
|
content: jsxRuntime.jsx(Export.ExportComponent, {
|
|
1641
2126
|
target: walletId,
|
|
1642
2127
|
exportType: base.ExportType.Wallet,
|
|
@@ -1648,18 +2133,22 @@ const ClientProvider = ({
|
|
|
1648
2133
|
} : {})
|
|
1649
2134
|
})
|
|
1650
2135
|
});
|
|
1651
|
-
};
|
|
1652
|
-
const handleExportPrivateKey = async params => {
|
|
2136
|
+
}, [pushPage]);
|
|
2137
|
+
const handleExportPrivateKey = react.useCallback(async params => {
|
|
1653
2138
|
const {
|
|
1654
2139
|
privateKeyId,
|
|
1655
2140
|
targetPublicKey,
|
|
2141
|
+
keyFormat,
|
|
1656
2142
|
stampWith
|
|
1657
2143
|
} = params;
|
|
1658
2144
|
pushPage({
|
|
1659
|
-
key: "Export
|
|
2145
|
+
key: "Export private key",
|
|
1660
2146
|
content: jsxRuntime.jsx(Export.ExportComponent, {
|
|
1661
2147
|
target: privateKeyId,
|
|
1662
2148
|
exportType: base.ExportType.PrivateKey,
|
|
2149
|
+
...(keyFormat !== undefined ? {
|
|
2150
|
+
keyFormat
|
|
2151
|
+
} : {}),
|
|
1663
2152
|
...(targetPublicKey !== undefined ? {
|
|
1664
2153
|
targetPublicKey
|
|
1665
2154
|
} : {}),
|
|
@@ -1668,18 +2157,22 @@ const ClientProvider = ({
|
|
|
1668
2157
|
} : {})
|
|
1669
2158
|
})
|
|
1670
2159
|
});
|
|
1671
|
-
};
|
|
1672
|
-
const handleExportWalletAccount = async params => {
|
|
2160
|
+
}, [pushPage]);
|
|
2161
|
+
const handleExportWalletAccount = react.useCallback(async params => {
|
|
1673
2162
|
const {
|
|
1674
2163
|
address,
|
|
1675
2164
|
targetPublicKey,
|
|
2165
|
+
keyFormat,
|
|
1676
2166
|
stampWith
|
|
1677
2167
|
} = params;
|
|
1678
2168
|
pushPage({
|
|
1679
|
-
key: "Export
|
|
2169
|
+
key: "Export wallet account",
|
|
1680
2170
|
content: jsxRuntime.jsx(Export.ExportComponent, {
|
|
1681
2171
|
target: address,
|
|
1682
2172
|
exportType: base.ExportType.WalletAccount,
|
|
2173
|
+
...(keyFormat !== undefined ? {
|
|
2174
|
+
keyFormat
|
|
2175
|
+
} : {}),
|
|
1683
2176
|
...(targetPublicKey !== undefined ? {
|
|
1684
2177
|
targetPublicKey
|
|
1685
2178
|
} : {}),
|
|
@@ -1688,8 +2181,8 @@ const ClientProvider = ({
|
|
|
1688
2181
|
} : {})
|
|
1689
2182
|
})
|
|
1690
2183
|
});
|
|
1691
|
-
};
|
|
1692
|
-
const handleImportWallet = async params => {
|
|
2184
|
+
}, [pushPage]);
|
|
2185
|
+
const handleImportWallet = react.useCallback(async params => {
|
|
1693
2186
|
const {
|
|
1694
2187
|
defaultWalletAccounts,
|
|
1695
2188
|
successPageDuration = 2000,
|
|
@@ -1697,8 +2190,9 @@ const ClientProvider = ({
|
|
|
1697
2190
|
} = params || {};
|
|
1698
2191
|
try {
|
|
1699
2192
|
return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
|
|
1700
|
-
key: "Import
|
|
2193
|
+
key: "Import wallet",
|
|
1701
2194
|
content: jsxRuntime.jsx(Import.ImportComponent, {
|
|
2195
|
+
importType: base.ImportType.Wallet,
|
|
1702
2196
|
onError: error => {
|
|
1703
2197
|
reject(error);
|
|
1704
2198
|
},
|
|
@@ -1720,8 +2214,41 @@ const ClientProvider = ({
|
|
|
1720
2214
|
}
|
|
1721
2215
|
throw new sdkTypes.TurnkeyError("Failed to import wallet.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
|
|
1722
2216
|
}
|
|
1723
|
-
};
|
|
1724
|
-
const
|
|
2217
|
+
}, [pushPage]);
|
|
2218
|
+
const handleImportPrivateKey = react.useCallback(async params => {
|
|
2219
|
+
const {
|
|
2220
|
+
curve,
|
|
2221
|
+
addressFormats,
|
|
2222
|
+
successPageDuration = 2000,
|
|
2223
|
+
stampWith
|
|
2224
|
+
} = params || {};
|
|
2225
|
+
try {
|
|
2226
|
+
return utils.withTurnkeyErrorHandling(() => new Promise((resolve, reject) => pushPage({
|
|
2227
|
+
key: "Import private key",
|
|
2228
|
+
content: jsxRuntime.jsx(Import.ImportComponent, {
|
|
2229
|
+
importType: base.ImportType.PrivateKey,
|
|
2230
|
+
curve: curve,
|
|
2231
|
+
addressFormats: addressFormats,
|
|
2232
|
+
onError: error => {
|
|
2233
|
+
reject(error);
|
|
2234
|
+
},
|
|
2235
|
+
onSuccess: privateKeyId => resolve(privateKeyId),
|
|
2236
|
+
...(successPageDuration !== undefined && {
|
|
2237
|
+
successPageDuration
|
|
2238
|
+
}),
|
|
2239
|
+
...(stampWith !== undefined && {
|
|
2240
|
+
stampWith
|
|
2241
|
+
})
|
|
2242
|
+
})
|
|
2243
|
+
})));
|
|
2244
|
+
} catch (error) {
|
|
2245
|
+
if (error instanceof sdkTypes.TurnkeyError) {
|
|
2246
|
+
throw error;
|
|
2247
|
+
}
|
|
2248
|
+
throw new sdkTypes.TurnkeyError("Failed to import private key.", sdkTypes.TurnkeyErrorCodes.IMPORT_WALLET_ERROR, error);
|
|
2249
|
+
}
|
|
2250
|
+
}, [pushPage]);
|
|
2251
|
+
const handleUpdateUserName = react.useCallback(async params => {
|
|
1725
2252
|
const {
|
|
1726
2253
|
successPageDuration = 2000,
|
|
1727
2254
|
subTitle,
|
|
@@ -1786,8 +2313,8 @@ const ClientProvider = ({
|
|
|
1786
2313
|
}
|
|
1787
2314
|
throw new sdkTypes.TurnkeyError("Failed to update user name.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_NAME_ERROR, error);
|
|
1788
2315
|
}
|
|
1789
|
-
};
|
|
1790
|
-
const handleUpdateUserPhoneNumber = async params => {
|
|
2316
|
+
}, [pushPage]);
|
|
2317
|
+
const handleUpdateUserPhoneNumber = react.useCallback(async params => {
|
|
1791
2318
|
const {
|
|
1792
2319
|
successPageDuration = 2000,
|
|
1793
2320
|
subTitle,
|
|
@@ -1890,8 +2417,8 @@ const ClientProvider = ({
|
|
|
1890
2417
|
}
|
|
1891
2418
|
throw new sdkTypes.TurnkeyError("Failed to initialize OTP for sms verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
|
|
1892
2419
|
}
|
|
1893
|
-
};
|
|
1894
|
-
const handleUpdateUserEmail = async params => {
|
|
2420
|
+
}, [pushPage]);
|
|
2421
|
+
const handleUpdateUserEmail = react.useCallback(async params => {
|
|
1895
2422
|
const {
|
|
1896
2423
|
successPageDuration = 2000,
|
|
1897
2424
|
subTitle,
|
|
@@ -1987,8 +2514,8 @@ const ClientProvider = ({
|
|
|
1987
2514
|
}
|
|
1988
2515
|
throw new sdkTypes.TurnkeyError("Failed to initialize OTP for email verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
|
|
1989
2516
|
}
|
|
1990
|
-
};
|
|
1991
|
-
const handleAddEmail = async params => {
|
|
2517
|
+
}, [pushPage]);
|
|
2518
|
+
const handleAddEmail = react.useCallback(async params => {
|
|
1992
2519
|
const {
|
|
1993
2520
|
successPageDuration = 2000,
|
|
1994
2521
|
subTitle,
|
|
@@ -2084,8 +2611,8 @@ const ClientProvider = ({
|
|
|
2084
2611
|
}
|
|
2085
2612
|
throw new sdkTypes.TurnkeyError("Failed to initialize OTP for email verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
|
|
2086
2613
|
}
|
|
2087
|
-
};
|
|
2088
|
-
const handleAddPhoneNumber = async params => {
|
|
2614
|
+
}, [pushPage]);
|
|
2615
|
+
const handleAddPhoneNumber = react.useCallback(async params => {
|
|
2089
2616
|
const {
|
|
2090
2617
|
successPageDuration = 2000,
|
|
2091
2618
|
subTitle,
|
|
@@ -2192,8 +2719,8 @@ const ClientProvider = ({
|
|
|
2192
2719
|
}
|
|
2193
2720
|
throw new sdkTypes.TurnkeyError("Failed to initialize OTP for sms verification.", sdkTypes.TurnkeyErrorCodes.INIT_OTP_ERROR, error);
|
|
2194
2721
|
}
|
|
2195
|
-
};
|
|
2196
|
-
const handleRemovePasskey = async params => {
|
|
2722
|
+
}, [pushPage]);
|
|
2723
|
+
const handleRemovePasskey = react.useCallback(async params => {
|
|
2197
2724
|
const {
|
|
2198
2725
|
authenticatorId,
|
|
2199
2726
|
successPageDuration = 2000,
|
|
@@ -2233,8 +2760,8 @@ const ClientProvider = ({
|
|
|
2233
2760
|
preventBack: true
|
|
2234
2761
|
});
|
|
2235
2762
|
}), callbacks, "Failed to remove passkey");
|
|
2236
|
-
};
|
|
2237
|
-
const handleAddPasskey = async params => {
|
|
2763
|
+
}, [pushPage]);
|
|
2764
|
+
const handleAddPasskey = react.useCallback(async params => {
|
|
2238
2765
|
const {
|
|
2239
2766
|
name,
|
|
2240
2767
|
displayName,
|
|
@@ -2278,8 +2805,8 @@ const ClientProvider = ({
|
|
|
2278
2805
|
}
|
|
2279
2806
|
throw new sdkTypes.TurnkeyError("Failed to add passkey in handler.", sdkTypes.TurnkeyErrorCodes.ADD_PASSKEY_ERROR, error);
|
|
2280
2807
|
}
|
|
2281
|
-
};
|
|
2282
|
-
const handleRemoveOauthProvider = async params => {
|
|
2808
|
+
}, [pushPage]);
|
|
2809
|
+
const handleRemoveOauthProvider = react.useCallback(async params => {
|
|
2283
2810
|
const {
|
|
2284
2811
|
providerId,
|
|
2285
2812
|
successPageDuration = 2000,
|
|
@@ -2322,8 +2849,8 @@ const ClientProvider = ({
|
|
|
2322
2849
|
}
|
|
2323
2850
|
throw new sdkTypes.TurnkeyError("Failed to remove OAuth provider in handler.", sdkTypes.TurnkeyErrorCodes.REMOVE_OAUTH_PROVIDER_ERROR, error);
|
|
2324
2851
|
}
|
|
2325
|
-
};
|
|
2326
|
-
const handleAddOauthProvider = async params => {
|
|
2852
|
+
}, [pushPage]);
|
|
2853
|
+
const handleAddOauthProvider = react.useCallback(async params => {
|
|
2327
2854
|
if (!client) throw new sdkTypes.TurnkeyError("Client is not initialized.", sdkTypes.TurnkeyErrorCodes.CLIENT_NOT_INITIALIZED);
|
|
2328
2855
|
if (!session) {
|
|
2329
2856
|
throw new sdkTypes.TurnkeyError("No active session found.", sdkTypes.TurnkeyErrorCodes.NO_SESSION_FOUND);
|
|
@@ -2352,6 +2879,22 @@ const ClientProvider = ({
|
|
|
2352
2879
|
});
|
|
2353
2880
|
};
|
|
2354
2881
|
switch (providerName) {
|
|
2882
|
+
case sdkTypes.OAuthProviders.DISCORD:
|
|
2883
|
+
{
|
|
2884
|
+
await handleDiscordOauth({
|
|
2885
|
+
openInPage: false,
|
|
2886
|
+
onOauthSuccess
|
|
2887
|
+
});
|
|
2888
|
+
break;
|
|
2889
|
+
}
|
|
2890
|
+
case sdkTypes.OAuthProviders.X:
|
|
2891
|
+
{
|
|
2892
|
+
await handleXOauth({
|
|
2893
|
+
openInPage: false,
|
|
2894
|
+
onOauthSuccess
|
|
2895
|
+
});
|
|
2896
|
+
break;
|
|
2897
|
+
}
|
|
2355
2898
|
case sdkTypes.OAuthProviders.GOOGLE:
|
|
2356
2899
|
{
|
|
2357
2900
|
await handleGoogleOauth({
|
|
@@ -2381,8 +2924,8 @@ const ClientProvider = ({
|
|
|
2381
2924
|
throw new sdkTypes.TurnkeyError(`Unsupported OAuth provider: ${providerName}`, sdkTypes.TurnkeyErrorCodes.NOT_FOUND);
|
|
2382
2925
|
}
|
|
2383
2926
|
}
|
|
2384
|
-
};
|
|
2385
|
-
const
|
|
2927
|
+
}, [pushPage]);
|
|
2928
|
+
const handleConnectExternalWallet = react.useCallback(async params => {
|
|
2386
2929
|
const {
|
|
2387
2930
|
successPageDuration = 2000
|
|
2388
2931
|
} = params || {};
|
|
@@ -2395,14 +2938,14 @@ const ClientProvider = ({
|
|
|
2395
2938
|
}
|
|
2396
2939
|
const providers = await getWalletProviders();
|
|
2397
2940
|
pushPage({
|
|
2398
|
-
key: "
|
|
2399
|
-
content: jsxRuntime.jsx(
|
|
2941
|
+
key: "Connect wallet",
|
|
2942
|
+
content: jsxRuntime.jsx(ConnectWallet.ConnectWalletModal, {
|
|
2400
2943
|
providers: providers,
|
|
2401
2944
|
successPageDuration: successPageDuration
|
|
2402
2945
|
})
|
|
2403
2946
|
});
|
|
2404
|
-
};
|
|
2405
|
-
const handleRemoveUserEmail = async params => {
|
|
2947
|
+
}, [pushPage]);
|
|
2948
|
+
const handleRemoveUserEmail = react.useCallback(async params => {
|
|
2406
2949
|
const {
|
|
2407
2950
|
successPageDuration = 2000,
|
|
2408
2951
|
stampWith,
|
|
@@ -2440,8 +2983,8 @@ const ClientProvider = ({
|
|
|
2440
2983
|
}
|
|
2441
2984
|
throw new sdkTypes.TurnkeyError("Failed to remove user email.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_EMAIL_ERROR, error);
|
|
2442
2985
|
}
|
|
2443
|
-
};
|
|
2444
|
-
const handleRemoveUserPhoneNumber = async params => {
|
|
2986
|
+
}, [pushPage]);
|
|
2987
|
+
const handleRemoveUserPhoneNumber = react.useCallback(async params => {
|
|
2445
2988
|
const {
|
|
2446
2989
|
successPageDuration = 2000,
|
|
2447
2990
|
stampWith,
|
|
@@ -2479,7 +3022,91 @@ const ClientProvider = ({
|
|
|
2479
3022
|
}
|
|
2480
3023
|
throw new sdkTypes.TurnkeyError("Failed to remove user phone number.", sdkTypes.TurnkeyErrorCodes.UPDATE_USER_PHONE_NUMBER_ERROR, error);
|
|
2481
3024
|
}
|
|
2482
|
-
};
|
|
3025
|
+
}, [pushPage]);
|
|
3026
|
+
react.useEffect(() => {
|
|
3027
|
+
if (proxyAuthConfigRef.current) return;
|
|
3028
|
+
// Only fetch the proxy auth config once. Use that to build the master config.
|
|
3029
|
+
const fetchProxyAuthConfig = async () => {
|
|
3030
|
+
try {
|
|
3031
|
+
let proxyAuthConfig;
|
|
3032
|
+
if (config.authProxyConfigId) {
|
|
3033
|
+
// Only fetch the proxy auth config if we have an authProxyId. This is a way for devs to explicitly disable the proxy auth.
|
|
3034
|
+
proxyAuthConfig = await core.getAuthProxyConfig(config.authProxyConfigId, config.authProxyUrl);
|
|
3035
|
+
proxyAuthConfigRef.current = proxyAuthConfig;
|
|
3036
|
+
}
|
|
3037
|
+
setMasterConfig(buildConfig(proxyAuthConfig));
|
|
3038
|
+
} catch {
|
|
3039
|
+
setClientState(base.ClientState.Error);
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
fetchProxyAuthConfig();
|
|
3043
|
+
}, []);
|
|
3044
|
+
react.useEffect(() => {
|
|
3045
|
+
// Start the client initialization process once we have the master config.
|
|
3046
|
+
if (!masterConfig) return;
|
|
3047
|
+
initializeClient();
|
|
3048
|
+
}, [masterConfig]);
|
|
3049
|
+
react.useEffect(() => {
|
|
3050
|
+
// Handle changes to the passed in config prop -- update the master config
|
|
3051
|
+
// If the proxyAuthConfigRef is already set, we don't need to fetch it again. Rebuild the master config with the updated config and stored proxyAuthConfig
|
|
3052
|
+
if (!proxyAuthConfigRef.current && config.authProxyConfigId) return;
|
|
3053
|
+
setMasterConfig(buildConfig(proxyAuthConfigRef.current ?? undefined));
|
|
3054
|
+
}, [config, proxyAuthConfigRef.current]);
|
|
3055
|
+
/**
|
|
3056
|
+
* @internal
|
|
3057
|
+
* We create `debouncedRefreshWallets()` so that multiple rapid wallet events
|
|
3058
|
+
* (for example, on Solana a single disconnect can emit several events we listen for)
|
|
3059
|
+
* only trigger `refreshWallets()` once.
|
|
3060
|
+
*
|
|
3061
|
+
* Defining the debounced function outside of the `useEffect` ensures all event
|
|
3062
|
+
* listeners in `initializeProviders` share the same instance, instead of creating
|
|
3063
|
+
* a new one on every render.
|
|
3064
|
+
*/
|
|
3065
|
+
const debouncedRefreshWallets = utils.useDebouncedCallback(refreshWallets, 100);
|
|
3066
|
+
react.useEffect(() => {
|
|
3067
|
+
if (!client) return;
|
|
3068
|
+
const handleRefreshWallets = async () => {
|
|
3069
|
+
// we only refresh the wallets if there is an active session
|
|
3070
|
+
// this is needed because a disconnect event can occur
|
|
3071
|
+
// while the user is unauthenticated
|
|
3072
|
+
if (session) {
|
|
3073
|
+
// this will update our walletProvider state
|
|
3074
|
+
await debouncedRefreshWallets();
|
|
3075
|
+
}
|
|
3076
|
+
};
|
|
3077
|
+
let cleanup = () => {};
|
|
3078
|
+
initializeWalletProviderListeners(walletProviders, handleRefreshWallets).then(fn => {
|
|
3079
|
+
cleanup = fn;
|
|
3080
|
+
}).catch(err => {
|
|
3081
|
+
console.error("Failed to init providers:", err);
|
|
3082
|
+
});
|
|
3083
|
+
return () => {
|
|
3084
|
+
cleanup();
|
|
3085
|
+
};
|
|
3086
|
+
}, [client, walletProviders]);
|
|
3087
|
+
react.useEffect(() => {
|
|
3088
|
+
// authState must be consistent with session state. We found during testing that there are cases where the session and authState can be out of sync in very rare edge cases.
|
|
3089
|
+
// This will ensure that they are always in sync and remove the need to setAuthState manually in other places.
|
|
3090
|
+
if (session && utils.isValidSession(session)) {
|
|
3091
|
+
setAuthState(base.AuthState.Authenticated);
|
|
3092
|
+
} else {
|
|
3093
|
+
setAuthState(base.AuthState.Unauthenticated);
|
|
3094
|
+
}
|
|
3095
|
+
}, [session]);
|
|
3096
|
+
react.useEffect(() => {
|
|
3097
|
+
// This will handle any redirect based oAuth. It then initializes the session. This is the last step before client is considered "ready"
|
|
3098
|
+
if (!client || !masterConfig) return;
|
|
3099
|
+
completeRedirectOauth().finally(() => {
|
|
3100
|
+
clearSessionTimeouts();
|
|
3101
|
+
initializeSessions().finally(() => {
|
|
3102
|
+
// Set the client state to ready only after all initializations are done.
|
|
3103
|
+
setClientState(base.ClientState.Ready);
|
|
3104
|
+
});
|
|
3105
|
+
});
|
|
3106
|
+
return () => {
|
|
3107
|
+
clearSessionTimeouts();
|
|
3108
|
+
};
|
|
3109
|
+
}, [client]);
|
|
2483
3110
|
return jsxRuntime.jsx(Types.ClientContext.Provider, {
|
|
2484
3111
|
value: {
|
|
2485
3112
|
session,
|
|
@@ -2497,7 +3124,7 @@ const ClientProvider = ({
|
|
|
2497
3124
|
getWalletProviders,
|
|
2498
3125
|
connectWalletAccount,
|
|
2499
3126
|
disconnectWalletAccount,
|
|
2500
|
-
|
|
3127
|
+
switchWalletAccountChain,
|
|
2501
3128
|
loginWithWallet,
|
|
2502
3129
|
signUpWithWallet,
|
|
2503
3130
|
loginOrSignupWithWallet,
|
|
@@ -2511,6 +3138,7 @@ const ClientProvider = ({
|
|
|
2511
3138
|
completeOauth,
|
|
2512
3139
|
fetchWallets,
|
|
2513
3140
|
fetchWalletAccounts,
|
|
3141
|
+
fetchPrivateKeys,
|
|
2514
3142
|
refreshWallets,
|
|
2515
3143
|
signMessage,
|
|
2516
3144
|
signTransaction,
|
|
@@ -2547,12 +3175,15 @@ const ClientProvider = ({
|
|
|
2547
3175
|
getProxyAuthConfig,
|
|
2548
3176
|
handleLogin,
|
|
2549
3177
|
handleGoogleOauth,
|
|
3178
|
+
handleXOauth,
|
|
3179
|
+
handleDiscordOauth,
|
|
2550
3180
|
handleAppleOauth,
|
|
2551
3181
|
handleFacebookOauth,
|
|
2552
3182
|
handleExportWallet,
|
|
2553
3183
|
handleExportPrivateKey,
|
|
2554
3184
|
handleExportWalletAccount,
|
|
2555
3185
|
handleImportWallet,
|
|
3186
|
+
handleImportPrivateKey,
|
|
2556
3187
|
handleUpdateUserEmail,
|
|
2557
3188
|
handleUpdateUserPhoneNumber,
|
|
2558
3189
|
handleUpdateUserName,
|
|
@@ -2563,7 +3194,7 @@ const ClientProvider = ({
|
|
|
2563
3194
|
handleAddEmail,
|
|
2564
3195
|
handleAddPhoneNumber,
|
|
2565
3196
|
handleSignMessage,
|
|
2566
|
-
|
|
3197
|
+
handleConnectExternalWallet,
|
|
2567
3198
|
handleRemoveUserEmail,
|
|
2568
3199
|
handleRemoveUserPhoneNumber
|
|
2569
3200
|
},
|