@dynamic-labs/sdk-react-core 0.18.1 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.cjs +2 -2
  3. package/package.js +2 -2
  4. package/package.json +9 -9
  5. package/src/index.cjs +2 -0
  6. package/src/index.js +1 -0
  7. package/src/lib/context/DynamicContext/DynamicContext.cjs +2 -1
  8. package/src/lib/context/DynamicContext/DynamicContext.js +2 -1
  9. package/src/lib/data/api.cjs +17 -1
  10. package/src/lib/data/api.d.ts +2 -1
  11. package/src/lib/data/api.js +17 -2
  12. package/src/lib/styles/index.shadow.cjs +1 -1
  13. package/src/lib/styles/index.shadow.js +1 -1
  14. package/src/lib/utils/functions/digestSHA256/digestSHA256.cjs +11 -0
  15. package/src/lib/utils/functions/digestSHA256/digestSHA256.d.ts +1 -0
  16. package/src/lib/utils/functions/digestSHA256/digestSHA256.js +7 -0
  17. package/src/lib/utils/functions/digestSHA256/index.d.ts +1 -0
  18. package/src/lib/utils/functions/encodeBase64URL/encodeBase64URL.cjs +14 -0
  19. package/src/lib/utils/functions/encodeBase64URL/encodeBase64URL.d.ts +1 -0
  20. package/src/lib/utils/functions/encodeBase64URL/encodeBase64URL.js +10 -0
  21. package/src/lib/utils/functions/encodeBase64URL/index.d.ts +1 -0
  22. package/src/lib/utils/functions/getRedirectURIOrigin/getRedirectURIOrigin.cjs +33 -0
  23. package/src/lib/utils/functions/getRedirectURIOrigin/getRedirectURIOrigin.d.ts +1 -0
  24. package/src/lib/utils/functions/getRedirectURIOrigin/getRedirectURIOrigin.js +29 -0
  25. package/src/lib/utils/functions/getRedirectURIOrigin/index.d.ts +1 -0
  26. package/src/lib/utils/functions/index.d.ts +5 -0
  27. package/src/lib/utils/functions/randomString/index.d.ts +1 -0
  28. package/src/lib/utils/functions/randomString/randomString.cjs +14 -0
  29. package/src/lib/utils/functions/randomString/randomString.d.ts +1 -0
  30. package/src/lib/utils/functions/randomString/randomString.js +10 -0
  31. package/src/lib/utils/functions/stringifyURIQuery/index.d.ts +1 -0
  32. package/src/lib/utils/functions/stringifyURIQuery/stringifyURIQuery.cjs +13 -0
  33. package/src/lib/utils/functions/stringifyURIQuery/stringifyURIQuery.d.ts +1 -0
  34. package/src/lib/utils/functions/stringifyURIQuery/stringifyURIQuery.js +9 -0
  35. package/src/lib/utils/hooks/useConnectedWalletsNetworkValidation/useConnectedWalletsNetworkValidation.cjs +7 -6
  36. package/src/lib/utils/hooks/useConnectedWalletsNetworkValidation/useConnectedWalletsNetworkValidation.js +7 -6
  37. package/src/lib/utils/hooks/useSocialAccounts/useSocialAccounts.cjs +90 -23
  38. package/src/lib/utils/hooks/useSocialAccounts/useSocialAccounts.js +92 -25
  39. package/src/lib/views/NetworkNotSupported/NetworkNotSupported.cjs +3 -2
  40. package/src/lib/views/NetworkNotSupported/NetworkNotSupported.js +3 -2
  41. package/src/lib/views/NetworkNotSupportedSwitchManual/NetworkNotSupportedSwitchManual.cjs +3 -2
  42. package/src/lib/views/NetworkNotSupportedSwitchManual/NetworkNotSupportedSwitchManual.js +3 -2
  43. package/src/lib/widgets/DynamicWidget/hooks/useFetchCurrency/useFetchCurrency.cjs +4 -1
  44. package/src/lib/widgets/DynamicWidget/hooks/useFetchCurrency/useFetchCurrency.js +4 -1
  45. package/src/lib/widgets/OAuthIFrameContainer/OAuthIFrameContainer.cjs +13 -0
  46. package/src/lib/widgets/OAuthIFrameContainer/OAuthIFrameContainer.d.ts +2 -0
  47. package/src/lib/widgets/OAuthIFrameContainer/OAuthIFrameContainer.js +9 -0
  48. package/src/lib/widgets/OAuthIFrameContainer/index.d.ts +1 -0
  49. package/src/lib/widgets/index.d.ts +1 -0
@@ -22,7 +22,7 @@ var isProgrammableNetworkSwitchSupported = require('../../functions/isProgrammab
22
22
 
23
23
  const useConnectedWalletsNetworkValidation = () => {
24
24
  const { connectedWallets, selectedWalletConnector, setSelectedWalletConnector, setShowAuthFlow, showAuthFlow, walletsToConnectByChain, isBridgeFlow, } = useInternalDynamicContext.useInternalDynamicContext();
25
- const { view, setView } = ViewContext.useViewContext();
25
+ const { setView } = ViewContext.useViewContext();
26
26
  React.useEffect(() => {
27
27
  if (!isBridgeFlow) {
28
28
  return;
@@ -53,16 +53,17 @@ const useConnectedWalletsNetworkValidation = () => {
53
53
  }
54
54
  setView(networkNotSupportedView);
55
55
  }
56
+ // we need it to remove 'showAuthFlow' property. we don't want to react to it,
57
+ // because closing modal on network switch causes it to re-open
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
56
59
  }, [
57
- isBridgeFlow,
58
60
  connectedWallets,
59
- view,
61
+ isBridgeFlow,
60
62
  selectedWalletConnector,
61
- walletsToConnectByChain,
62
- showAuthFlow,
63
- setView,
64
63
  setSelectedWalletConnector,
65
64
  setShowAuthFlow,
65
+ setView,
66
+ walletsToConnectByChain,
66
67
  ]);
67
68
  };
68
69
 
@@ -18,7 +18,7 @@ import { isProgrammableNetworkSwitchSupported } from '../../functions/isProgramm
18
18
 
19
19
  const useConnectedWalletsNetworkValidation = () => {
20
20
  const { connectedWallets, selectedWalletConnector, setSelectedWalletConnector, setShowAuthFlow, showAuthFlow, walletsToConnectByChain, isBridgeFlow, } = useInternalDynamicContext();
21
- const { view, setView } = useViewContext();
21
+ const { setView } = useViewContext();
22
22
  useEffect(() => {
23
23
  if (!isBridgeFlow) {
24
24
  return;
@@ -49,16 +49,17 @@ const useConnectedWalletsNetworkValidation = () => {
49
49
  }
50
50
  setView(networkNotSupportedView);
51
51
  }
52
+ // we need it to remove 'showAuthFlow' property. we don't want to react to it,
53
+ // because closing modal on network switch causes it to re-open
54
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52
55
  }, [
53
- isBridgeFlow,
54
56
  connectedWallets,
55
- view,
57
+ isBridgeFlow,
56
58
  selectedWalletConnector,
57
- walletsToConnectByChain,
58
- showAuthFlow,
59
- setView,
60
59
  setSelectedWalletConnector,
61
60
  setShowAuthFlow,
61
+ setView,
62
+ walletsToConnectByChain,
62
63
  ]);
63
64
  };
64
65
 
@@ -23,6 +23,11 @@ require('@dynamic-labs/multi-wallet');
23
23
  var getAuthToken = require('../../functions/getAuthToken/getAuthToken.cjs');
24
24
  var index = require('../../functions/getEnabledOAuthProviders/index.cjs');
25
25
  var isSocialKycEnabled = require('../../functions/isSocialKycEnabled/isSocialKycEnabled.cjs');
26
+ var randomString = require('../../functions/randomString/randomString.cjs');
27
+ var stringifyURIQuery = require('../../functions/stringifyURIQuery/stringifyURIQuery.cjs');
28
+ var digestSHA256 = require('../../functions/digestSHA256/digestSHA256.cjs');
29
+ var encodeBase64URL = require('../../functions/encodeBase64URL/encodeBase64URL.cjs');
30
+ var getRedirectURIOrigin = require('../../functions/getRedirectURIOrigin/getRedirectURIOrigin.cjs');
26
31
 
27
32
  const socialProviders = [
28
33
  'discord',
@@ -33,12 +38,16 @@ const socialProviders = [
33
38
  'twitch',
34
39
  'twitter',
35
40
  ];
41
+ // pkce params are only required for twitter and break if added for other providers, like google
42
+ // so we should only add provider that support pkce to this list
43
+ const providersRequiringPkce = ['twitter', 'github'];
36
44
  const useSocialAccounts = () => {
37
45
  var _a;
38
46
  const { environmentId, user, projectSettings, onboardingOnlyJwt } = useInternalDynamicContext.useInternalDynamicContext();
39
47
  const { updateUserProfileByJWT } = useUpdateUserProfileByJWTCallback.useUpdateUserProfileByJWTCallback();
40
48
  const [isProcessing, setIsProcessing] = React.useState(false);
41
49
  const [error, setError] = React.useState();
50
+ const providersWaitingOauthMessages = React.useRef({});
42
51
  const userJwt = (_a = getAuthToken.getAuthToken()) !== null && _a !== void 0 ? _a : onboardingOnlyJwt;
43
52
  const oauthProviders = index.getEnabledOAuthProviders(projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.providers);
44
53
  const verifiedOAuthCredentialsMap = React.useMemo(() => {
@@ -57,6 +66,23 @@ const useSocialAccounts = () => {
57
66
  setIsProcessing(false);
58
67
  setError(message);
59
68
  };
69
+ const addOAuthIFrame = (url) => {
70
+ const container = document.getElementById('oauth-iframe-container');
71
+ let oauthIFrame = document.getElementById('oauth-iframe');
72
+ if (!container || oauthIFrame) {
73
+ return;
74
+ }
75
+ oauthIFrame = document.createElement('iframe');
76
+ oauthIFrame.setAttribute('id', 'oauth-iframe');
77
+ oauthIFrame.setAttribute('src', url);
78
+ container.appendChild(oauthIFrame);
79
+ };
80
+ const removeOAuthIFrame = () => {
81
+ const oauthIFrame = document.getElementById('oauth-iframe');
82
+ if (oauthIFrame) {
83
+ oauthIFrame.remove();
84
+ }
85
+ };
60
86
  const checkValidProvider = (provider) => {
61
87
  if (!provider) {
62
88
  handleError('Provider is required');
@@ -68,38 +94,63 @@ const useSocialAccounts = () => {
68
94
  }
69
95
  return true;
70
96
  };
71
- const linkSocialAccount = (provider) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
72
- setError(undefined);
73
- setIsProcessing(true);
97
+ const checkValidOAuthLinking = (provider) => {
74
98
  if (!isSocialKycEnabled.isSocialKycEnabled(projectSettings)) {
75
99
  handleError('Social linking is not enabled on the dashboard');
76
- return;
100
+ return false;
77
101
  }
78
102
  if (!checkValidProvider(provider)) {
79
- return;
103
+ return false;
80
104
  }
81
105
  const verifiedCredential = verifiedOAuthCredentialsMap[provider];
82
106
  if (verifiedCredential) {
83
107
  handleError(`Account already linked for provider: ${provider}`);
84
- return;
108
+ return false;
85
109
  }
86
110
  const oauthProvider = oauthProviders.find((p) => p.provider === provider);
87
111
  if (!oauthProvider) {
88
112
  handleError(`Provider is not enabled on the dashboard: ${provider}`);
113
+ return false;
114
+ }
115
+ return true;
116
+ };
117
+ const linkSocialAccount = (provider) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
118
+ var _b;
119
+ setError(undefined);
120
+ setIsProcessing(true);
121
+ // invalid linking request
122
+ if (!checkValidOAuthLinking(provider)) {
89
123
  return;
90
124
  }
125
+ const state = randomString.randomString(8);
126
+ const verifier = randomString.randomString(43);
91
127
  const handleAuthWindowMessage = (event) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
92
- var _b, _c, _d;
93
- if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'authorization_response') {
94
- logger.logger.debug('Message received', { data: event.data });
128
+ const message = event.data;
129
+ const isAuthorizationMessage = (message === null || message === void 0 ? void 0 : message.type) === 'authorization_response';
130
+ if (isAuthorizationMessage) {
131
+ logger.logger.debug('Message received', { data: message });
132
+ }
133
+ const isValidMessage = isAuthorizationMessage && (message === null || message === void 0 ? void 0 : message.provider) === provider;
134
+ // don't process imvalid messages for provider
135
+ if (!isValidMessage) {
136
+ return;
95
137
  }
96
- if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) !== 'authorization_response' ||
97
- ((_d = event.data) === null || _d === void 0 ? void 0 : _d.provider) !== provider) {
138
+ removeOAuthIFrame();
139
+ window.removeEventListener('message', handleAuthWindowMessage);
140
+ providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: false });
141
+ const { code, error } = message;
142
+ if (error && error !== 'undefined') {
143
+ handleError(`Failed to link ${provider} social account: ${error}`);
144
+ return;
145
+ }
146
+ if (!code) {
147
+ handleError(`Failed to link ${provider} social account: no authorization code`);
98
148
  return;
99
149
  }
100
- const { code } = event.data;
101
150
  const jwt = yield api.verifyOAuth(environmentId, provider, {
102
151
  code,
152
+ codeVerifier: verifier,
153
+ state,
103
154
  }, String(userJwt));
104
155
  if (!jwt) {
105
156
  handleError(`Failed to link social account: ${provider}`);
@@ -108,18 +159,34 @@ const useSocialAccounts = () => {
108
159
  yield updateUserProfileByJWT(jwt);
109
160
  setIsProcessing(false);
110
161
  });
111
- window.addEventListener('message', handleAuthWindowMessage);
112
- const authWindow = window.open(oauthProvider.authorizationUrl, '_blank', 'width=500,height=600');
113
- const authWindowInterval = setInterval(() => {
114
- if (authWindow === null || authWindow === void 0 ? void 0 : authWindow.closed) {
115
- window.removeEventListener('message', handleAuthWindowMessage);
116
- clearInterval(authWindowInterval);
117
- setIsProcessing(false);
118
- }
119
- }, 1000);
162
+ const loginUrl = yield api.getOauthLoginUrl(environmentId, provider, String(userJwt));
163
+ if (!loginUrl) {
164
+ handleError(`Failed to get login url to link social account: ${provider}`);
165
+ return;
166
+ }
167
+ // we need to use an iframe to be the intermediary in communications between the popup and the sdk
168
+ // because of twitter limitations with accessing the window.opener and BroadcastChannel not working between
169
+ // different origins (our api redirect page and the app where the sdk is running)
170
+ const oauthIFrameUrl = `${getRedirectURIOrigin.getRedirectURIOrigin(loginUrl)}/sdkoauth`;
171
+ addOAuthIFrame(oauthIFrameUrl);
172
+ let oauthLoginUrl = loginUrl;
173
+ if (providersRequiringPkce.includes(provider)) {
174
+ const digest = yield digestSHA256.digestSHA256(verifier);
175
+ const pkceParams = stringifyURIQuery.stringifyURIQuery({
176
+ code_challenge: encodeBase64URL.encodeBase64URL(digest),
177
+ code_challenge_method: 'S256',
178
+ state: state,
179
+ });
180
+ oauthLoginUrl = `${loginUrl}&${pkceParams}`;
181
+ }
182
+ if (!((_b = providersWaitingOauthMessages.current) === null || _b === void 0 ? void 0 : _b[provider])) {
183
+ window.addEventListener('message', handleAuthWindowMessage);
184
+ providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: true });
185
+ }
186
+ window.open(oauthLoginUrl, '_blank', 'width=500,height=600');
120
187
  });
121
188
  const unlinkSocialAccount = (provider) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
122
- var _e;
189
+ var _c;
123
190
  setError(undefined);
124
191
  setIsProcessing(true);
125
192
  if (!checkValidProvider(provider)) {
@@ -133,7 +200,7 @@ const useSocialAccounts = () => {
133
200
  try {
134
201
  const jwt = yield api.unlinkOAuth({
135
202
  environmentId,
136
- verifiedCrentialId: (_e = verifiedCredential.id) !== null && _e !== void 0 ? _e : '',
203
+ verifiedCrentialId: (_c = verifiedCredential.id) !== null && _c !== void 0 ? _c : '',
137
204
  }, String(userJwt));
138
205
  if (!jwt) {
139
206
  handleError(`Failed to unlink social account: ${provider} | ${verifiedCredential.id}`);
@@ -1,7 +1,7 @@
1
1
  import { __awaiter } from '../../../../../_virtual/_tslib.js';
2
- import { useState, useMemo } from 'react';
2
+ import { useState, useRef, useMemo } from 'react';
3
3
  import { useInternalDynamicContext } from '../../../context/DynamicContext/useInternalDynamicContext.js';
4
- import { unlinkOAuth, verifyOAuth } from '../../../data/api.js';
4
+ import { getOauthLoginUrl, unlinkOAuth, verifyOAuth } from '../../../data/api.js';
5
5
  import { useUpdateUserProfileByJWTCallback } from '../useUpdateUserProfileByJWTCallback/useUpdateUserProfileByJWTCallback.js';
6
6
  import '@dynamic-labs/sdk-api';
7
7
  import { logger } from '../../../shared/logger.js';
@@ -19,6 +19,11 @@ import '@dynamic-labs/multi-wallet';
19
19
  import { getAuthToken } from '../../functions/getAuthToken/getAuthToken.js';
20
20
  import { getEnabledOAuthProviders } from '../../functions/getEnabledOAuthProviders/index.js';
21
21
  import { isSocialKycEnabled } from '../../functions/isSocialKycEnabled/isSocialKycEnabled.js';
22
+ import { randomString } from '../../functions/randomString/randomString.js';
23
+ import { stringifyURIQuery } from '../../functions/stringifyURIQuery/stringifyURIQuery.js';
24
+ import { digestSHA256 } from '../../functions/digestSHA256/digestSHA256.js';
25
+ import { encodeBase64URL } from '../../functions/encodeBase64URL/encodeBase64URL.js';
26
+ import { getRedirectURIOrigin } from '../../functions/getRedirectURIOrigin/getRedirectURIOrigin.js';
22
27
 
23
28
  const socialProviders = [
24
29
  'discord',
@@ -29,12 +34,16 @@ const socialProviders = [
29
34
  'twitch',
30
35
  'twitter',
31
36
  ];
37
+ // pkce params are only required for twitter and break if added for other providers, like google
38
+ // so we should only add provider that support pkce to this list
39
+ const providersRequiringPkce = ['twitter', 'github'];
32
40
  const useSocialAccounts = () => {
33
41
  var _a;
34
42
  const { environmentId, user, projectSettings, onboardingOnlyJwt } = useInternalDynamicContext();
35
43
  const { updateUserProfileByJWT } = useUpdateUserProfileByJWTCallback();
36
44
  const [isProcessing, setIsProcessing] = useState(false);
37
45
  const [error, setError] = useState();
46
+ const providersWaitingOauthMessages = useRef({});
38
47
  const userJwt = (_a = getAuthToken()) !== null && _a !== void 0 ? _a : onboardingOnlyJwt;
39
48
  const oauthProviders = getEnabledOAuthProviders(projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.providers);
40
49
  const verifiedOAuthCredentialsMap = useMemo(() => {
@@ -53,6 +62,23 @@ const useSocialAccounts = () => {
53
62
  setIsProcessing(false);
54
63
  setError(message);
55
64
  };
65
+ const addOAuthIFrame = (url) => {
66
+ const container = document.getElementById('oauth-iframe-container');
67
+ let oauthIFrame = document.getElementById('oauth-iframe');
68
+ if (!container || oauthIFrame) {
69
+ return;
70
+ }
71
+ oauthIFrame = document.createElement('iframe');
72
+ oauthIFrame.setAttribute('id', 'oauth-iframe');
73
+ oauthIFrame.setAttribute('src', url);
74
+ container.appendChild(oauthIFrame);
75
+ };
76
+ const removeOAuthIFrame = () => {
77
+ const oauthIFrame = document.getElementById('oauth-iframe');
78
+ if (oauthIFrame) {
79
+ oauthIFrame.remove();
80
+ }
81
+ };
56
82
  const checkValidProvider = (provider) => {
57
83
  if (!provider) {
58
84
  handleError('Provider is required');
@@ -64,38 +90,63 @@ const useSocialAccounts = () => {
64
90
  }
65
91
  return true;
66
92
  };
67
- const linkSocialAccount = (provider) => __awaiter(void 0, void 0, void 0, function* () {
68
- setError(undefined);
69
- setIsProcessing(true);
93
+ const checkValidOAuthLinking = (provider) => {
70
94
  if (!isSocialKycEnabled(projectSettings)) {
71
95
  handleError('Social linking is not enabled on the dashboard');
72
- return;
96
+ return false;
73
97
  }
74
98
  if (!checkValidProvider(provider)) {
75
- return;
99
+ return false;
76
100
  }
77
101
  const verifiedCredential = verifiedOAuthCredentialsMap[provider];
78
102
  if (verifiedCredential) {
79
103
  handleError(`Account already linked for provider: ${provider}`);
80
- return;
104
+ return false;
81
105
  }
82
106
  const oauthProvider = oauthProviders.find((p) => p.provider === provider);
83
107
  if (!oauthProvider) {
84
108
  handleError(`Provider is not enabled on the dashboard: ${provider}`);
109
+ return false;
110
+ }
111
+ return true;
112
+ };
113
+ const linkSocialAccount = (provider) => __awaiter(void 0, void 0, void 0, function* () {
114
+ var _b;
115
+ setError(undefined);
116
+ setIsProcessing(true);
117
+ // invalid linking request
118
+ if (!checkValidOAuthLinking(provider)) {
85
119
  return;
86
120
  }
121
+ const state = randomString(8);
122
+ const verifier = randomString(43);
87
123
  const handleAuthWindowMessage = (event) => __awaiter(void 0, void 0, void 0, function* () {
88
- var _b, _c, _d;
89
- if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'authorization_response') {
90
- logger.debug('Message received', { data: event.data });
124
+ const message = event.data;
125
+ const isAuthorizationMessage = (message === null || message === void 0 ? void 0 : message.type) === 'authorization_response';
126
+ if (isAuthorizationMessage) {
127
+ logger.debug('Message received', { data: message });
128
+ }
129
+ const isValidMessage = isAuthorizationMessage && (message === null || message === void 0 ? void 0 : message.provider) === provider;
130
+ // don't process imvalid messages for provider
131
+ if (!isValidMessage) {
132
+ return;
91
133
  }
92
- if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) !== 'authorization_response' ||
93
- ((_d = event.data) === null || _d === void 0 ? void 0 : _d.provider) !== provider) {
134
+ removeOAuthIFrame();
135
+ window.removeEventListener('message', handleAuthWindowMessage);
136
+ providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: false });
137
+ const { code, error } = message;
138
+ if (error && error !== 'undefined') {
139
+ handleError(`Failed to link ${provider} social account: ${error}`);
140
+ return;
141
+ }
142
+ if (!code) {
143
+ handleError(`Failed to link ${provider} social account: no authorization code`);
94
144
  return;
95
145
  }
96
- const { code } = event.data;
97
146
  const jwt = yield verifyOAuth(environmentId, provider, {
98
147
  code,
148
+ codeVerifier: verifier,
149
+ state,
99
150
  }, String(userJwt));
100
151
  if (!jwt) {
101
152
  handleError(`Failed to link social account: ${provider}`);
@@ -104,18 +155,34 @@ const useSocialAccounts = () => {
104
155
  yield updateUserProfileByJWT(jwt);
105
156
  setIsProcessing(false);
106
157
  });
107
- window.addEventListener('message', handleAuthWindowMessage);
108
- const authWindow = window.open(oauthProvider.authorizationUrl, '_blank', 'width=500,height=600');
109
- const authWindowInterval = setInterval(() => {
110
- if (authWindow === null || authWindow === void 0 ? void 0 : authWindow.closed) {
111
- window.removeEventListener('message', handleAuthWindowMessage);
112
- clearInterval(authWindowInterval);
113
- setIsProcessing(false);
114
- }
115
- }, 1000);
158
+ const loginUrl = yield getOauthLoginUrl(environmentId, provider, String(userJwt));
159
+ if (!loginUrl) {
160
+ handleError(`Failed to get login url to link social account: ${provider}`);
161
+ return;
162
+ }
163
+ // we need to use an iframe to be the intermediary in communications between the popup and the sdk
164
+ // because of twitter limitations with accessing the window.opener and BroadcastChannel not working between
165
+ // different origins (our api redirect page and the app where the sdk is running)
166
+ const oauthIFrameUrl = `${getRedirectURIOrigin(loginUrl)}/sdkoauth`;
167
+ addOAuthIFrame(oauthIFrameUrl);
168
+ let oauthLoginUrl = loginUrl;
169
+ if (providersRequiringPkce.includes(provider)) {
170
+ const digest = yield digestSHA256(verifier);
171
+ const pkceParams = stringifyURIQuery({
172
+ code_challenge: encodeBase64URL(digest),
173
+ code_challenge_method: 'S256',
174
+ state: state,
175
+ });
176
+ oauthLoginUrl = `${loginUrl}&${pkceParams}`;
177
+ }
178
+ if (!((_b = providersWaitingOauthMessages.current) === null || _b === void 0 ? void 0 : _b[provider])) {
179
+ window.addEventListener('message', handleAuthWindowMessage);
180
+ providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: true });
181
+ }
182
+ window.open(oauthLoginUrl, '_blank', 'width=500,height=600');
116
183
  });
117
184
  const unlinkSocialAccount = (provider) => __awaiter(void 0, void 0, void 0, function* () {
118
- var _e;
185
+ var _c;
119
186
  setError(undefined);
120
187
  setIsProcessing(true);
121
188
  if (!checkValidProvider(provider)) {
@@ -129,7 +196,7 @@ const useSocialAccounts = () => {
129
196
  try {
130
197
  const jwt = yield unlinkOAuth({
131
198
  environmentId,
132
- verifiedCrentialId: (_e = verifiedCredential.id) !== null && _e !== void 0 ? _e : '',
199
+ verifiedCrentialId: (_c = verifiedCredential.id) !== null && _c !== void 0 ? _c : '',
133
200
  }, String(userJwt));
134
201
  if (!jwt) {
135
202
  handleError(`Failed to unlink social account: ${provider} | ${verifiedCredential.id}`);
@@ -53,12 +53,13 @@ var SelectNetworkButton = require('../../components/SelectNetworkButton/SelectNe
53
53
  var useApplyWalletConnectorNetwork = require('../../utils/hooks/useApplyWalletConnectorNetwork/useApplyWalletConnectorNetwork.cjs');
54
54
 
55
55
  const NetworkNotSupported = () => {
56
- const { network, onboardingImageUrl, selectedWalletConnector: walletConnector, connectedWallets, primaryWallet, isFullyConnected, setShowAuthFlow, } = useInternalDynamicContext.useInternalDynamicContext();
56
+ const { network, onboardingImageUrl, selectedWalletConnector: walletConnector, connectedWallets, primaryWallet, isFullyConnected, setShowAuthFlow, walletsToConnectByChain, } = useInternalDynamicContext.useInternalDynamicContext();
57
57
  // Refresh selectedWalletConnector network when primaryWallet or connectedWallets updates
58
58
  const { data: walletConnectorNetwork } = usePromise.usePromise(() => walletConnector === null || walletConnector === void 0 ? void 0 : walletConnector.getNetwork(), { deps: [primaryWallet, walletConnector, connectedWallets] });
59
59
  const finishConnectAndSign = useConnectAndSign.useConnectAndSign({
60
60
  shouldCallCallback: false,
61
- shouldUpdateWallets: !isFullyConnected,
61
+ shouldUpdateWallets: !isFullyConnected ||
62
+ (isFullyConnected && Boolean(walletsToConnectByChain === null || walletsToConnectByChain === void 0 ? void 0 : walletsToConnectByChain.length)),
62
63
  });
63
64
  const [isNetworkPickerOpen, setIsNetworkPickerOpen] = React.useState(false);
64
65
  // Finishes auth flow when wallet switches to proper network
@@ -49,12 +49,13 @@ import { SelectNetworkButton } from '../../components/SelectNetworkButton/Select
49
49
  import { useApplyWalletConnectorNetwork } from '../../utils/hooks/useApplyWalletConnectorNetwork/useApplyWalletConnectorNetwork.js';
50
50
 
51
51
  const NetworkNotSupported = () => {
52
- const { network, onboardingImageUrl, selectedWalletConnector: walletConnector, connectedWallets, primaryWallet, isFullyConnected, setShowAuthFlow, } = useInternalDynamicContext();
52
+ const { network, onboardingImageUrl, selectedWalletConnector: walletConnector, connectedWallets, primaryWallet, isFullyConnected, setShowAuthFlow, walletsToConnectByChain, } = useInternalDynamicContext();
53
53
  // Refresh selectedWalletConnector network when primaryWallet or connectedWallets updates
54
54
  const { data: walletConnectorNetwork } = usePromise(() => walletConnector === null || walletConnector === void 0 ? void 0 : walletConnector.getNetwork(), { deps: [primaryWallet, walletConnector, connectedWallets] });
55
55
  const finishConnectAndSign = useConnectAndSign({
56
56
  shouldCallCallback: false,
57
- shouldUpdateWallets: !isFullyConnected,
57
+ shouldUpdateWallets: !isFullyConnected ||
58
+ (isFullyConnected && Boolean(walletsToConnectByChain === null || walletsToConnectByChain === void 0 ? void 0 : walletsToConnectByChain.length)),
58
59
  });
59
60
  const [isNetworkPickerOpen, setIsNetworkPickerOpen] = useState(false);
60
61
  // Finishes auth flow when wallet switches to proper network
@@ -54,14 +54,15 @@ var useApplyWalletConnectorNetwork = require('../../utils/hooks/useApplyWalletCo
54
54
  // as the wallet does not support programmatic network change.
55
55
  const NetworkNotSupportedSwitchManual = () => {
56
56
  var _a, _b;
57
- const { user, selectedWalletConnector: walletConnector, connectedWallets, isFullyConnected, setShowAuthFlow, } = useInternalDynamicContext.useInternalDynamicContext();
57
+ const { user, selectedWalletConnector: walletConnector, connectedWallets, isFullyConnected, setShowAuthFlow, walletsToConnectByChain, } = useInternalDynamicContext.useInternalDynamicContext();
58
58
  // Refresh selectedWalletConnector network when primaryWallet or connectedWallets updates
59
59
  const { data: walletConnectorNetwork } = usePromise.usePromise(() => walletConnector === null || walletConnector === void 0 ? void 0 : walletConnector.getNetwork(), {
60
60
  deps: [walletConnector, connectedWallets],
61
61
  });
62
62
  const finishConnectAndSign = useConnectAndSign.useConnectAndSign({
63
63
  shouldCallCallback: false,
64
- shouldUpdateWallets: !isFullyConnected,
64
+ shouldUpdateWallets: !isFullyConnected ||
65
+ (isFullyConnected && Boolean(walletsToConnectByChain === null || walletsToConnectByChain === void 0 ? void 0 : walletsToConnectByChain.length)),
65
66
  });
66
67
  // Finishes auth flow when wallet switches to proper network
67
68
  React.useEffect(() => {
@@ -50,14 +50,15 @@ import { useApplyWalletConnectorNetwork } from '../../utils/hooks/useApplyWallet
50
50
  // as the wallet does not support programmatic network change.
51
51
  const NetworkNotSupportedSwitchManual = () => {
52
52
  var _a, _b;
53
- const { user, selectedWalletConnector: walletConnector, connectedWallets, isFullyConnected, setShowAuthFlow, } = useInternalDynamicContext();
53
+ const { user, selectedWalletConnector: walletConnector, connectedWallets, isFullyConnected, setShowAuthFlow, walletsToConnectByChain, } = useInternalDynamicContext();
54
54
  // Refresh selectedWalletConnector network when primaryWallet or connectedWallets updates
55
55
  const { data: walletConnectorNetwork } = usePromise(() => walletConnector === null || walletConnector === void 0 ? void 0 : walletConnector.getNetwork(), {
56
56
  deps: [walletConnector, connectedWallets],
57
57
  });
58
58
  const finishConnectAndSign = useConnectAndSign({
59
59
  shouldCallCallback: false,
60
- shouldUpdateWallets: !isFullyConnected,
60
+ shouldUpdateWallets: !isFullyConnected ||
61
+ (isFullyConnected && Boolean(walletsToConnectByChain === null || walletsToConnectByChain === void 0 ? void 0 : walletsToConnectByChain.length)),
61
62
  });
62
63
  // Finishes auth flow when wallet switches to proper network
63
64
  useEffect(() => {
@@ -18,7 +18,10 @@ const useFetchCurrency = (connector, network) => {
18
18
  setCurrency(chainInfo === null || chainInfo === void 0 ? void 0 : chainInfo.symbol);
19
19
  return;
20
20
  }
21
- const result = (_b = connector.evmNetworks) === null || _b === void 0 ? void 0 : _b.find((element) => element.chainId === chainId);
21
+ const network = connector.connectedChain === 'STARK'
22
+ ? 'starknetNetworks'
23
+ : 'evmNetworks';
24
+ const result = (_b = connector[network]) === null || _b === void 0 ? void 0 : _b.find((element) => element.chainId === chainId);
22
25
  setCurrency((_d = (_c = result === null || result === void 0 ? void 0 : result.nativeCurrency) === null || _c === void 0 ? void 0 : _c.symbol) !== null && _d !== void 0 ? _d : chainInfo === null || chainInfo === void 0 ? void 0 : chainInfo.symbol);
23
26
  return [currency];
24
27
  });
@@ -14,7 +14,10 @@ const useFetchCurrency = (connector, network) => {
14
14
  setCurrency(chainInfo === null || chainInfo === void 0 ? void 0 : chainInfo.symbol);
15
15
  return;
16
16
  }
17
- const result = (_b = connector.evmNetworks) === null || _b === void 0 ? void 0 : _b.find((element) => element.chainId === chainId);
17
+ const network = connector.connectedChain === 'STARK'
18
+ ? 'starknetNetworks'
19
+ : 'evmNetworks';
20
+ const result = (_b = connector[network]) === null || _b === void 0 ? void 0 : _b.find((element) => element.chainId === chainId);
18
21
  setCurrency((_d = (_c = result === null || result === void 0 ? void 0 : result.nativeCurrency) === null || _c === void 0 ? void 0 : _c.symbol) !== null && _d !== void 0 ? _d : chainInfo === null || chainInfo === void 0 ? void 0 : chainInfo.symbol);
19
22
  return [currency];
20
23
  });
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // we need to use an iframe to be the intermediary in communications between the oauth popup and the sdk
8
+ // because of twitter limitations with accessing the window.opener and BroadcastChannel not working between
9
+ // different origins (our api redirect page and the app where the sdk is running)
10
+ // https://linear.app/dynamic-labs/issue/GVTY-17/[frontend]-fix-twitter-linking
11
+ const OAuthIFrameContainer = () => (jsxRuntime.jsx("div", { id: 'oauth-iframe-container', "data-testid": 'oauth-iframe-container', style: { height: 0, visibility: 'hidden', width: 0 } }));
12
+
13
+ exports.OAuthIFrameContainer = OAuthIFrameContainer;
@@ -0,0 +1,2 @@
1
+ /// <reference types="react" />
2
+ export declare const OAuthIFrameContainer: () => JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+
3
+ // we need to use an iframe to be the intermediary in communications between the oauth popup and the sdk
4
+ // because of twitter limitations with accessing the window.opener and BroadcastChannel not working between
5
+ // different origins (our api redirect page and the app where the sdk is running)
6
+ // https://linear.app/dynamic-labs/issue/GVTY-17/[frontend]-fix-twitter-linking
7
+ const OAuthIFrameContainer = () => (jsx("div", { id: 'oauth-iframe-container', "data-testid": 'oauth-iframe-container', style: { height: 0, visibility: 'hidden', width: 0 } }));
8
+
9
+ export { OAuthIFrameContainer };
@@ -0,0 +1 @@
1
+ export { OAuthIFrameContainer } from './OAuthIFrameContainer';
@@ -1,3 +1,4 @@
1
1
  export * from './DynamicWidget';
2
2
  export * from './FundingWidget';
3
3
  export * from './DynamicBridgeWidget';
4
+ export * from './OAuthIFrameContainer';