@calimero-network/mero-react 3.0.1 → 3.0.2

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/index.cjs CHANGED
@@ -45,6 +45,28 @@ var ConnectionType = /* @__PURE__ */ ((ConnectionType2) => {
45
45
  return ConnectionType2;
46
46
  })(ConnectionType || {});
47
47
 
48
+ // src/auth/node-trust.ts
49
+ function sameOrigin(a, b) {
50
+ try {
51
+ return new URL(a).origin === new URL(b).origin;
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+ function resolveTrustedNodeUrl(params) {
57
+ const { candidate, initiated, allowedNodeUrls } = params;
58
+ if (!candidate) {
59
+ return { url: initiated ?? null, rejected: false };
60
+ }
61
+ if (initiated) {
62
+ return sameOrigin(candidate, initiated) ? { url: candidate, rejected: false } : { url: null, rejected: true };
63
+ }
64
+ if (allowedNodeUrls && allowedNodeUrls.length > 0) {
65
+ return allowedNodeUrls.some((u) => sameOrigin(candidate, u)) ? { url: candidate, rejected: false } : { url: null, rejected: true };
66
+ }
67
+ return { url: null, rejected: true };
68
+ }
69
+
48
70
  // src/storage/index.ts
49
71
  var STORAGE_KEYS = {
50
72
  ACCESS_TOKEN: "mero:access_token",
@@ -175,13 +197,6 @@ function clearAllStorage() {
175
197
  }
176
198
  var MeroContext = react.createContext(null);
177
199
  var isBrowser = typeof window !== "undefined";
178
- var _tokenStore = null;
179
- function getTokenStore() {
180
- if (!_tokenStore) {
181
- _tokenStore = new meroJs.LocalStorageTokenStore();
182
- }
183
- return _tokenStore;
184
- }
185
200
  function getPermissionsForMode(mode) {
186
201
  switch (mode) {
187
202
  case "single-context" /* SingleContext */:
@@ -215,8 +230,14 @@ function MeroProvider({
215
230
  packageName,
216
231
  packageVersion,
217
232
  registryUrl,
218
- timeoutMs = 3e4
233
+ timeoutMs = 3e4,
234
+ allowedNodeUrls,
235
+ tokenStore: tokenStoreProp
219
236
  }) {
237
+ const tokenStore = react.useMemo(
238
+ () => tokenStoreProp ?? new meroJs.LocalStorageTokenStore(),
239
+ [tokenStoreProp]
240
+ );
220
241
  const [mero, setMero] = react.useState(null);
221
242
  const [isAuthenticated, setIsAuthenticated] = react.useState(false);
222
243
  const [isOnline, setIsOnline] = react.useState(true);
@@ -239,13 +260,13 @@ function MeroProvider({
239
260
  }
240
261
  const instance = new meroJs.MeroJs({
241
262
  baseUrl: url,
242
- tokenStore: getTokenStore(),
263
+ tokenStore,
243
264
  timeoutMs
244
265
  });
245
266
  meroRef.current = instance;
246
267
  return instance;
247
268
  },
248
- [timeoutMs]
269
+ [timeoutMs, tokenStore]
249
270
  );
250
271
  const checkAuth = react.useCallback(async (instance) => {
251
272
  try {
@@ -279,6 +300,7 @@ function MeroProvider({
279
300
  meroRef.current.clearToken();
280
301
  meroRef.current.close();
281
302
  }
303
+ tokenStore.clear();
282
304
  clearAllStorage();
283
305
  setMero(null);
284
306
  setIsAuthenticated(false);
@@ -287,39 +309,50 @@ function MeroProvider({
287
309
  setContextIdState(null);
288
310
  setContextIdentityState(null);
289
311
  meroRef.current = null;
290
- }, []);
312
+ }, [tokenStore]);
291
313
  react.useEffect(() => {
292
314
  let active = true;
293
315
  const init = async () => {
294
316
  const callback = callbackRef.current;
317
+ let nodeFromCallback = null;
295
318
  if (callback) {
296
- getTokenStore().setTokens({
297
- access_token: callback.accessToken,
298
- refresh_token: callback.refreshToken,
299
- expires_at: parseJwtExpiry(callback.accessToken)
319
+ const { url, rejected } = resolveTrustedNodeUrl({
320
+ candidate: callback.nodeUrl,
321
+ initiated: getNodeUrl(),
322
+ allowedNodeUrls
300
323
  });
301
- if (callback.applicationId) {
302
- setApplicationId(callback.applicationId);
303
- if (active) setApplicationIdState(callback.applicationId);
304
- }
305
- if (callback.contextId) {
306
- setContextId(callback.contextId);
307
- if (active) setContextIdState(callback.contextId);
308
- }
309
- if (callback.contextIdentity) {
310
- setContextIdentity(callback.contextIdentity);
311
- if (active) setContextIdentityState(callback.contextIdentity);
312
- }
313
- if (callback.nodeUrl) {
314
- setNodeUrl(callback.nodeUrl);
315
- if (active) setNodeUrlState(callback.nodeUrl);
316
- }
317
324
  if (isBrowser) {
318
325
  window.history.replaceState({}, "", window.location.pathname + window.location.search);
319
326
  }
320
327
  callbackRef.current = null;
328
+ if (rejected) {
329
+ console.error(
330
+ "[MeroProvider] OAuth callback node_url is not trusted (it does not match the node login was initiated with, nor `allowedNodeUrls`). Ignoring the callback; no tokens stored."
331
+ );
332
+ } else if (url) {
333
+ tokenStore.setTokens({
334
+ access_token: callback.accessToken,
335
+ refresh_token: callback.refreshToken,
336
+ expires_at: parseJwtExpiry(callback.accessToken)
337
+ });
338
+ if (callback.applicationId) {
339
+ setApplicationId(callback.applicationId);
340
+ if (active) setApplicationIdState(callback.applicationId);
341
+ }
342
+ if (callback.contextId) {
343
+ setContextId(callback.contextId);
344
+ if (active) setContextIdState(callback.contextId);
345
+ }
346
+ if (callback.contextIdentity) {
347
+ setContextIdentity(callback.contextIdentity);
348
+ if (active) setContextIdentityState(callback.contextIdentity);
349
+ }
350
+ setNodeUrl(url);
351
+ if (active) setNodeUrlState(url);
352
+ nodeFromCallback = url;
353
+ }
321
354
  }
322
- const savedUrl = callback?.nodeUrl || getNodeUrl();
355
+ const savedUrl = nodeFromCallback || getNodeUrl();
323
356
  if (!savedUrl) {
324
357
  if (active) setIsLoading(false);
325
358
  return;
@@ -331,7 +364,7 @@ function MeroProvider({
331
364
  setMero(instance);
332
365
  setIsAuthenticated(true);
333
366
  setIsOnline(true);
334
- } else if (callback) {
367
+ } else if (nodeFromCallback) {
335
368
  setMero(instance);
336
369
  }
337
370
  setIsLoading(false);
@@ -343,7 +376,7 @@ function MeroProvider({
343
376
  return () => {
344
377
  active = false;
345
378
  };
346
- }, [createMeroInstance, checkAuth]);
379
+ }, [createMeroInstance, checkAuth, allowedNodeUrls, tokenStore]);
347
380
  react.useEffect(() => {
348
381
  if (!isAuthenticated || !meroRef.current) return;
349
382
  let active = true;