@dynamic-labs/sdk-react-core 0.18.100-viem.28 → 0.18.100-viem.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
1
 
2
+ ### [0.18.100-viem.29](https://github.com/dynamic-labs/DynamicAuth/compare/v0.18.100-viem.28...v0.18.100-viem.29) (2023-11-22)
3
+
4
+
5
+ ### Bug Fixes
6
+
7
+ * allow metadata to be updated in updateUser hook ([#3947](https://github.com/dynamic-labs/DynamicAuth/issues/3947)) ([d6a3591](https://github.com/dynamic-labs/DynamicAuth/commit/d6a35918327b095d1a715942bc84cd9763e6864d))
8
+ * null safe check to window object ([#3919](https://github.com/dynamic-labs/DynamicAuth/issues/3919)) ([e466964](https://github.com/dynamic-labs/DynamicAuth/commit/e4669644c451139d443e534df238602d18fcda07))
9
+ * only process oauth message coming from the expected origin ([#3812](https://github.com/dynamic-labs/DynamicAuth/issues/3812)) ([ff98948](https://github.com/dynamic-labs/DynamicAuth/commit/ff98948b5415967ad3769887bb95d9c6d353a0c6))
10
+
2
11
  ### [0.18.100-viem.28](https://github.com/dynamic-labs/DynamicAuth/compare/v0.18.100-viem.27...v0.18.100-viem.28) (2023-10-30)
3
12
 
4
13
 
package/package.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version = "0.18.100-viem.28";
5
+ var version = "0.18.100-viem.29";
6
6
  var dependencies = {
7
7
  "react-focus-lock": "2.9.2",
8
8
  "@dynamic-labs/iconic": "0.1.32",
package/package.js CHANGED
@@ -1,4 +1,4 @@
1
- var version = "0.18.100-viem.28";
1
+ var version = "0.18.100-viem.29";
2
2
  var dependencies = {
3
3
  "react-focus-lock": "2.9.2",
4
4
  "@dynamic-labs/iconic": "0.1.32",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynamic-labs/sdk-react-core",
3
- "version": "0.18.100-viem.28",
3
+ "version": "0.18.100-viem.29",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/dynamic-labs/DynamicAuth.git",
@@ -15,13 +15,13 @@
15
15
  "yup": "0.32.11",
16
16
  "formik": "2.2.9",
17
17
  "qrcode": "1.5.1",
18
- "@dynamic-labs/logger": "0.18.100-viem.28",
19
- "@dynamic-labs/multi-wallet": "0.18.100-viem.28",
20
- "@dynamic-labs/rpc-providers": "0.18.100-viem.28",
21
- "@dynamic-labs/types": "0.18.100-viem.28",
22
- "@dynamic-labs/utils": "0.18.100-viem.28",
23
- "@dynamic-labs/wallet-book": "0.18.100-viem.28",
24
- "@dynamic-labs/wallet-connector-core": "0.18.100-viem.28",
18
+ "@dynamic-labs/logger": "0.18.100-viem.29",
19
+ "@dynamic-labs/multi-wallet": "0.18.100-viem.29",
20
+ "@dynamic-labs/rpc-providers": "0.18.100-viem.29",
21
+ "@dynamic-labs/types": "0.18.100-viem.29",
22
+ "@dynamic-labs/utils": "0.18.100-viem.29",
23
+ "@dynamic-labs/wallet-book": "0.18.100-viem.29",
24
+ "@dynamic-labs/wallet-connector-core": "0.18.100-viem.29",
25
25
  "eventemitter3": "5.0.1"
26
26
  },
27
27
  "devDependencies": {
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const getProviderByType = (providers, providerType) => providers.find((p) => p.provider === providerType);
6
+
7
+ exports.getProviderByType = getProviderByType;
@@ -0,0 +1,2 @@
1
+ import { Provider, ProviderEnum } from '@dynamic-labs/sdk-api';
2
+ export declare const getProviderByType: (providers: Provider[], providerType: ProviderEnum) => Provider | undefined;
@@ -0,0 +1,3 @@
1
+ const getProviderByType = (providers, providerType) => providers.find((p) => p.provider === providerType);
2
+
3
+ export { getProviderByType };
@@ -0,0 +1 @@
1
+ export { getProviderByType } from './getProviderByType';
@@ -28,3 +28,4 @@ export * from './digestSHA256';
28
28
  export * from './encodeBase64URL';
29
29
  export * from './getRedirectURIOrigin';
30
30
  export * from './sendTransactionWithAutoNonce';
31
+ export * from './getProviderByType';
@@ -29,6 +29,7 @@ var stringifyURIQuery = require('../../functions/stringifyURIQuery/stringifyURIQ
29
29
  var digestSHA256 = require('../../functions/digestSHA256/digestSHA256.cjs');
30
30
  var encodeBase64URL = require('../../functions/encodeBase64URL/encodeBase64URL.cjs');
31
31
  var getRedirectURIOrigin = require('../../functions/getRedirectURIOrigin/getRedirectURIOrigin.cjs');
32
+ var getProviderByType = require('../../functions/getProviderByType/getProviderByType.cjs');
32
33
 
33
34
  const socialProviders = [
34
35
  'discord',
@@ -136,28 +137,52 @@ const useSocialAccounts = ({ sessionTimeout } = {
136
137
  providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: false });
137
138
  };
138
139
  const handleAuthWindowMessage = (event) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
139
- var _c;
140
+ var _c, _d;
140
141
  const message = event.data;
141
142
  const isAuthorizationMessage = (message === null || message === void 0 ? void 0 : message.type) === 'authorization_response';
142
143
  if (isAuthorizationMessage) {
143
144
  logger.logger.debug('Message received', { data: message });
144
145
  }
145
- const isValidMessage = isAuthorizationMessage && (message === null || message === void 0 ? void 0 : message.provider) === provider;
146
+ const apiProvider = getProviderByType.getProviderByType((_c = projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.providers) !== null && _c !== void 0 ? _c : [], provider);
147
+ if (!(apiProvider === null || apiProvider === void 0 ? void 0 : apiProvider.redirectUrl)) {
148
+ return;
149
+ }
150
+ let expectedOrigin = '';
151
+ try {
152
+ const redirectUri = new URL(apiProvider.redirectUrl);
153
+ expectedOrigin = redirectUri.origin;
154
+ }
155
+ catch (e) {
156
+ logger.logger.error('Failed to parse social provider redirect url', {
157
+ error: e,
158
+ });
159
+ return;
160
+ }
161
+ const isExpectedOrigin = event.origin === expectedOrigin;
162
+ const isValidMessage = isAuthorizationMessage &&
163
+ (message === null || message === void 0 ? void 0 : message.provider) === provider &&
164
+ isExpectedOrigin;
146
165
  // don't process imvalid messages for provider
147
166
  if (!isValidMessage) {
148
167
  return;
149
168
  }
150
169
  setIsProcessing(true);
151
- if (!((_c = providersWaitingOauthMessages.current) === null || _c === void 0 ? void 0 : _c[provider])) {
170
+ if (!((_d = providersWaitingOauthMessages.current) === null || _d === void 0 ? void 0 : _d[provider])) {
152
171
  handleError(`Linking ${provider} account session timeout.`);
153
172
  return;
154
173
  }
155
174
  clearListeners();
156
- const { code, error } = message;
175
+ const { code, error, state: authState } = message;
157
176
  if (error && error !== 'undefined') {
158
177
  handleError(`Failed to link ${provider} social account: ${error}`);
159
178
  return;
160
179
  }
180
+ // check that the state we receive from message is the same state we calculated earlier
181
+ // this could be an attack
182
+ if (state !== authState) {
183
+ handleError(`Failed to link ${provider} social account: invalid auth state`);
184
+ return;
185
+ }
161
186
  if (!code) {
162
187
  handleError(`Failed to link ${provider} social account: no authorization code`);
163
188
  return;
@@ -184,19 +209,23 @@ const useSocialAccounts = ({ sessionTimeout } = {
184
209
  // different origins (our api redirect page and the app where the sdk is running)
185
210
  const oauthIFrameUrl = `${getRedirectURIOrigin.getRedirectURIOrigin(loginUrl)}/sdkoauth`;
186
211
  addOAuthIFrame(oauthIFrameUrl);
187
- let oauthLoginUrl = loginUrl;
212
+ if (!((_b = providersWaitingOauthMessages.current) === null || _b === void 0 ? void 0 : _b[provider])) {
213
+ window.addEventListener('message', handleAuthWindowMessage);
214
+ providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: true });
215
+ }
216
+ // add state param for all providers
217
+ const stateParam = stringifyURIQuery.stringifyURIQuery({
218
+ state,
219
+ });
220
+ let oauthLoginUrl = `${loginUrl}&${stateParam}`;
221
+ // only add pkce params for providers that support/require it
188
222
  if (providersRequiringPkce.includes(provider)) {
189
223
  const digest = yield digestSHA256.digestSHA256(verifier);
190
224
  const pkceParams = stringifyURIQuery.stringifyURIQuery({
191
225
  code_challenge: encodeBase64URL.encodeBase64URL(digest),
192
226
  code_challenge_method: 'S256',
193
- state: state,
194
227
  });
195
- oauthLoginUrl = `${loginUrl}&${pkceParams}`;
196
- }
197
- if (!((_b = providersWaitingOauthMessages.current) === null || _b === void 0 ? void 0 : _b[provider])) {
198
- window.addEventListener('message', handleAuthWindowMessage);
199
- providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: true });
228
+ oauthLoginUrl = `${oauthLoginUrl}&${pkceParams}`;
200
229
  }
201
230
  const authWindow = window.open(oauthLoginUrl, '_blank', 'width=500,height=600');
202
231
  if (!providersWithoutWindowOpenerRefence.includes(provider)) {
@@ -225,7 +254,7 @@ const useSocialAccounts = ({ sessionTimeout } = {
225
254
  }
226
255
  });
227
256
  const unlinkSocialAccount = (provider) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
228
- var _d;
257
+ var _e;
229
258
  setError(undefined);
230
259
  setIsProcessing(true);
231
260
  if (!checkValidProvider(provider)) {
@@ -241,7 +270,7 @@ const useSocialAccounts = ({ sessionTimeout } = {
241
270
  const jwt = yield api.unlinkOAuth({
242
271
  environmentId,
243
272
  primaryWalletId,
244
- verifiedCrentialId: (_d = verifiedCredential.id) !== null && _d !== void 0 ? _d : '',
273
+ verifiedCrentialId: (_e = verifiedCredential.id) !== null && _e !== void 0 ? _e : '',
245
274
  }, String(userJwt));
246
275
  if (!jwt) {
247
276
  handleError(`Failed to unlink social account: ${provider} | ${verifiedCredential.id}`);
@@ -25,6 +25,7 @@ import { stringifyURIQuery } from '../../functions/stringifyURIQuery/stringifyUR
25
25
  import { digestSHA256 } from '../../functions/digestSHA256/digestSHA256.js';
26
26
  import { encodeBase64URL } from '../../functions/encodeBase64URL/encodeBase64URL.js';
27
27
  import { getRedirectURIOrigin } from '../../functions/getRedirectURIOrigin/getRedirectURIOrigin.js';
28
+ import { getProviderByType } from '../../functions/getProviderByType/getProviderByType.js';
28
29
 
29
30
  const socialProviders = [
30
31
  'discord',
@@ -132,28 +133,52 @@ const useSocialAccounts = ({ sessionTimeout } = {
132
133
  providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: false });
133
134
  };
134
135
  const handleAuthWindowMessage = (event) => __awaiter(void 0, void 0, void 0, function* () {
135
- var _c;
136
+ var _c, _d;
136
137
  const message = event.data;
137
138
  const isAuthorizationMessage = (message === null || message === void 0 ? void 0 : message.type) === 'authorization_response';
138
139
  if (isAuthorizationMessage) {
139
140
  logger.debug('Message received', { data: message });
140
141
  }
141
- const isValidMessage = isAuthorizationMessage && (message === null || message === void 0 ? void 0 : message.provider) === provider;
142
+ const apiProvider = getProviderByType((_c = projectSettings === null || projectSettings === void 0 ? void 0 : projectSettings.providers) !== null && _c !== void 0 ? _c : [], provider);
143
+ if (!(apiProvider === null || apiProvider === void 0 ? void 0 : apiProvider.redirectUrl)) {
144
+ return;
145
+ }
146
+ let expectedOrigin = '';
147
+ try {
148
+ const redirectUri = new URL(apiProvider.redirectUrl);
149
+ expectedOrigin = redirectUri.origin;
150
+ }
151
+ catch (e) {
152
+ logger.error('Failed to parse social provider redirect url', {
153
+ error: e,
154
+ });
155
+ return;
156
+ }
157
+ const isExpectedOrigin = event.origin === expectedOrigin;
158
+ const isValidMessage = isAuthorizationMessage &&
159
+ (message === null || message === void 0 ? void 0 : message.provider) === provider &&
160
+ isExpectedOrigin;
142
161
  // don't process imvalid messages for provider
143
162
  if (!isValidMessage) {
144
163
  return;
145
164
  }
146
165
  setIsProcessing(true);
147
- if (!((_c = providersWaitingOauthMessages.current) === null || _c === void 0 ? void 0 : _c[provider])) {
166
+ if (!((_d = providersWaitingOauthMessages.current) === null || _d === void 0 ? void 0 : _d[provider])) {
148
167
  handleError(`Linking ${provider} account session timeout.`);
149
168
  return;
150
169
  }
151
170
  clearListeners();
152
- const { code, error } = message;
171
+ const { code, error, state: authState } = message;
153
172
  if (error && error !== 'undefined') {
154
173
  handleError(`Failed to link ${provider} social account: ${error}`);
155
174
  return;
156
175
  }
176
+ // check that the state we receive from message is the same state we calculated earlier
177
+ // this could be an attack
178
+ if (state !== authState) {
179
+ handleError(`Failed to link ${provider} social account: invalid auth state`);
180
+ return;
181
+ }
157
182
  if (!code) {
158
183
  handleError(`Failed to link ${provider} social account: no authorization code`);
159
184
  return;
@@ -180,19 +205,23 @@ const useSocialAccounts = ({ sessionTimeout } = {
180
205
  // different origins (our api redirect page and the app where the sdk is running)
181
206
  const oauthIFrameUrl = `${getRedirectURIOrigin(loginUrl)}/sdkoauth`;
182
207
  addOAuthIFrame(oauthIFrameUrl);
183
- let oauthLoginUrl = loginUrl;
208
+ if (!((_b = providersWaitingOauthMessages.current) === null || _b === void 0 ? void 0 : _b[provider])) {
209
+ window.addEventListener('message', handleAuthWindowMessage);
210
+ providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: true });
211
+ }
212
+ // add state param for all providers
213
+ const stateParam = stringifyURIQuery({
214
+ state,
215
+ });
216
+ let oauthLoginUrl = `${loginUrl}&${stateParam}`;
217
+ // only add pkce params for providers that support/require it
184
218
  if (providersRequiringPkce.includes(provider)) {
185
219
  const digest = yield digestSHA256(verifier);
186
220
  const pkceParams = stringifyURIQuery({
187
221
  code_challenge: encodeBase64URL(digest),
188
222
  code_challenge_method: 'S256',
189
- state: state,
190
223
  });
191
- oauthLoginUrl = `${loginUrl}&${pkceParams}`;
192
- }
193
- if (!((_b = providersWaitingOauthMessages.current) === null || _b === void 0 ? void 0 : _b[provider])) {
194
- window.addEventListener('message', handleAuthWindowMessage);
195
- providersWaitingOauthMessages.current = Object.assign(Object.assign({}, providersWaitingOauthMessages.current), { [provider]: true });
224
+ oauthLoginUrl = `${oauthLoginUrl}&${pkceParams}`;
196
225
  }
197
226
  const authWindow = window.open(oauthLoginUrl, '_blank', 'width=500,height=600');
198
227
  if (!providersWithoutWindowOpenerRefence.includes(provider)) {
@@ -221,7 +250,7 @@ const useSocialAccounts = ({ sessionTimeout } = {
221
250
  }
222
251
  });
223
252
  const unlinkSocialAccount = (provider) => __awaiter(void 0, void 0, void 0, function* () {
224
- var _d;
253
+ var _e;
225
254
  setError(undefined);
226
255
  setIsProcessing(true);
227
256
  if (!checkValidProvider(provider)) {
@@ -237,7 +266,7 @@ const useSocialAccounts = ({ sessionTimeout } = {
237
266
  const jwt = yield unlinkOAuth({
238
267
  environmentId,
239
268
  primaryWalletId,
240
- verifiedCrentialId: (_d = verifiedCredential.id) !== null && _d !== void 0 ? _d : '',
269
+ verifiedCrentialId: (_e = verifiedCredential.id) !== null && _e !== void 0 ? _e : '',
241
270
  }, String(userJwt));
242
271
  if (!jwt) {
243
272
  handleError(`Failed to unlink social account: ${provider} | ${verifiedCredential.id}`);
@@ -40,6 +40,7 @@ const userFieldsSchema = yup.object().shape({
40
40
  kdaWallet: yup.string(),
41
41
  lastName: yup.string(),
42
42
  ltcWallet: yup.string(),
43
+ metadata: yup.object(),
43
44
  newsletterNotification: yup.boolean(),
44
45
  phoneNumber: yup.string(),
45
46
  tShirtSize: yup.string(),
@@ -55,10 +56,13 @@ const useUserUpdateRequestInternal = ({ validationSchemaStripUnknown, }) => {
55
56
  const { updateUserProfileByJWT } = useUpdateUserProfileByJWTCallback.useUpdateUserProfileByJWTCallback();
56
57
  const updateUser = React.useCallback((userFields, userEnvironmentId) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
57
58
  var _a, _b, _c;
59
+ // `stripUnknown: true` will automatically strip out any values in the metadata object
60
+ // so we need to pull out the metadata object here and then re-add it after stripping unknown fields
61
+ const { metadata } = userFields;
58
62
  const validatedUserFields = yield userFieldsSchema.validate(userFields, {
59
63
  stripUnknown: validationSchemaStripUnknown,
60
64
  });
61
- const updateUserProfileResponse = yield api.updateUserProfileFields(jwtToken, userEnvironmentId || environmentId, validatedUserFields);
65
+ const updateUserProfileResponse = yield api.updateUserProfileFields(jwtToken, userEnvironmentId || environmentId, Object.assign(Object.assign({}, validatedUserFields), { metadata }));
62
66
  // check if decoded JWT still somehow has missing fields, we can't proceed if it does
63
67
  const decodedJwt = decodeJwt.decodeJwt(updateUserProfileResponse.jwt);
64
68
  const missingFields = (_a = decodedJwt === null || decodedJwt === void 0 ? void 0 : decodedJwt.missingFields) !== null && _a !== void 0 ? _a : [];
@@ -36,6 +36,7 @@ const userFieldsSchema = object().shape({
36
36
  kdaWallet: string(),
37
37
  lastName: string(),
38
38
  ltcWallet: string(),
39
+ metadata: object(),
39
40
  newsletterNotification: boolean(),
40
41
  phoneNumber: string(),
41
42
  tShirtSize: string(),
@@ -51,10 +52,13 @@ const useUserUpdateRequestInternal = ({ validationSchemaStripUnknown, }) => {
51
52
  const { updateUserProfileByJWT } = useUpdateUserProfileByJWTCallback();
52
53
  const updateUser = useCallback((userFields, userEnvironmentId) => __awaiter(void 0, void 0, void 0, function* () {
53
54
  var _a, _b, _c;
55
+ // `stripUnknown: true` will automatically strip out any values in the metadata object
56
+ // so we need to pull out the metadata object here and then re-add it after stripping unknown fields
57
+ const { metadata } = userFields;
54
58
  const validatedUserFields = yield userFieldsSchema.validate(userFields, {
55
59
  stripUnknown: validationSchemaStripUnknown,
56
60
  });
57
- const updateUserProfileResponse = yield updateUserProfileFields(jwtToken, userEnvironmentId || environmentId, validatedUserFields);
61
+ const updateUserProfileResponse = yield updateUserProfileFields(jwtToken, userEnvironmentId || environmentId, Object.assign(Object.assign({}, validatedUserFields), { metadata }));
58
62
  // check if decoded JWT still somehow has missing fields, we can't proceed if it does
59
63
  const decodedJwt = decodeJwt(updateUserProfileResponse.jwt);
60
64
  const missingFields = (_a = decodedJwt === null || decodedJwt === void 0 ? void 0 : decodedJwt.missingFields) !== null && _a !== void 0 ? _a : [];