@calimero-network/mero-react 3.0.0 → 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 +82 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +84 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import React__default, { CSSProperties, ReactNode } from 'react';
|
|
4
4
|
import * as _calimero_network_mero_js from '@calimero-network/mero-js';
|
|
5
|
-
import { MeroJs, SetMetadataRequest, AddGroupMembersRequest, CreateContextRequest, CreateGroupInNamespaceRequest, CreateNamespaceRequest, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, DeleteGroupRequest, DeleteNamespaceRequest, DetachContextFromGroupRequest, GroupContextEntry, GroupInfo, CreateGroupInvitationRequest, GroupMember, MetadataRecord, GroupUpgradeStatusResponseData, JoinGroupRequest, JoinNamespaceRequest, MigrationStatus, MigrationStatusRollup, MemberMigrationStatusEntry, MigrateMyEntriesSummary, Namespace, SubgroupEntry, NamespaceIdentity, NestGroupRequest, RegisterGroupSigningKeyRequest, RemoveGroupMembersRequest, ResyncContextRequest, RetryGroupUpgradeRequest, SetDefaultCapabilitiesRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, SseEventData, SyncGroupRequest, UnnestGroupRequest, UpdateGroupSettingsRequest, UpdateMemberRoleRequest, UpgradeGroupRequest, TokenData } from '@calimero-network/mero-js';
|
|
5
|
+
import { MeroJs, TokenStore, SetMetadataRequest, AddGroupMembersRequest, CreateContextRequest, CreateGroupInNamespaceRequest, CreateNamespaceRequest, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, DeleteGroupRequest, DeleteNamespaceRequest, DetachContextFromGroupRequest, GroupContextEntry, GroupInfo, CreateGroupInvitationRequest, GroupMember, MetadataRecord, GroupUpgradeStatusResponseData, JoinGroupRequest, JoinNamespaceRequest, MigrationStatus, MigrationStatusRollup, MemberMigrationStatusEntry, MigrateMyEntriesSummary, Namespace, SubgroupEntry, NamespaceIdentity, NestGroupRequest, RegisterGroupSigningKeyRequest, RemoveGroupMembersRequest, ResyncContextRequest, RetryGroupUpgradeRequest, SetDefaultCapabilitiesRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, SseEventData, SyncGroupRequest, UnnestGroupRequest, UpdateGroupSettingsRequest, UpdateMemberRoleRequest, UpgradeGroupRequest, TokenData } from '@calimero-network/mero-js';
|
|
6
6
|
export * from '@calimero-network/mero-js';
|
|
7
7
|
export { CAPABILITIES, MetadataRecord, hasCap, withCap, withoutCap } from '@calimero-network/mero-js';
|
|
8
8
|
|
|
@@ -129,13 +129,32 @@ interface MeroProviderConfig {
|
|
|
129
129
|
registryUrl?: string;
|
|
130
130
|
/** Request timeout in milliseconds */
|
|
131
131
|
timeoutMs?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Origins the OAuth callback is allowed to authenticate against. Defends
|
|
134
|
+
* against a malicious `node_url` in the callback URL (token exfiltration):
|
|
135
|
+
* the node login was initiated with is always trusted, and this allowlist
|
|
136
|
+
* additionally permits direct-callback entry to known nodes. When neither is
|
|
137
|
+
* available (no initiated node and no allowlist) a callback node_url is
|
|
138
|
+
* rejected and a security error is logged.
|
|
139
|
+
*/
|
|
140
|
+
allowedNodeUrls?: string[];
|
|
141
|
+
/**
|
|
142
|
+
* Token store for access/refresh tokens. Defaults to a localStorage-backed
|
|
143
|
+
* store that persists across reloads.
|
|
144
|
+
*
|
|
145
|
+
* SECURITY: localStorage tokens — including the refresh token — are readable
|
|
146
|
+
* by any script on the page, so an XSS bug can exfiltrate them. For sensitive
|
|
147
|
+
* deployments pass a `MemoryTokenStore` (session-only) or a store backed by an
|
|
148
|
+
* HttpOnly cookie set by your auth service.
|
|
149
|
+
*/
|
|
150
|
+
tokenStore?: TokenStore;
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
declare const MeroContext: React__default.Context<MeroContextValue | null>;
|
|
135
154
|
interface MeroProviderProps extends MeroProviderConfig {
|
|
136
155
|
children: React__default.ReactNode;
|
|
137
156
|
}
|
|
138
|
-
declare function MeroProvider({ children, mode, packageName, packageVersion, registryUrl, timeoutMs, }: MeroProviderProps): react_jsx_runtime.JSX.Element;
|
|
157
|
+
declare function MeroProvider({ children, mode, packageName, packageVersion, registryUrl, timeoutMs, allowedNodeUrls, tokenStore: tokenStoreProp, }: MeroProviderProps): react_jsx_runtime.JSX.Element;
|
|
139
158
|
declare function useMero(): MeroContextValue;
|
|
140
159
|
|
|
141
160
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import React__default, { CSSProperties, ReactNode } from 'react';
|
|
4
4
|
import * as _calimero_network_mero_js from '@calimero-network/mero-js';
|
|
5
|
-
import { MeroJs, SetMetadataRequest, AddGroupMembersRequest, CreateContextRequest, CreateGroupInNamespaceRequest, CreateNamespaceRequest, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, DeleteGroupRequest, DeleteNamespaceRequest, DetachContextFromGroupRequest, GroupContextEntry, GroupInfo, CreateGroupInvitationRequest, GroupMember, MetadataRecord, GroupUpgradeStatusResponseData, JoinGroupRequest, JoinNamespaceRequest, MigrationStatus, MigrationStatusRollup, MemberMigrationStatusEntry, MigrateMyEntriesSummary, Namespace, SubgroupEntry, NamespaceIdentity, NestGroupRequest, RegisterGroupSigningKeyRequest, RemoveGroupMembersRequest, ResyncContextRequest, RetryGroupUpgradeRequest, SetDefaultCapabilitiesRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, SseEventData, SyncGroupRequest, UnnestGroupRequest, UpdateGroupSettingsRequest, UpdateMemberRoleRequest, UpgradeGroupRequest, TokenData } from '@calimero-network/mero-js';
|
|
5
|
+
import { MeroJs, TokenStore, SetMetadataRequest, AddGroupMembersRequest, CreateContextRequest, CreateGroupInNamespaceRequest, CreateNamespaceRequest, CreateNamespaceInvitationRequest, CreateNamespaceInvitationResponseData, CreateRecursiveInvitationResponseData, DeleteGroupRequest, DeleteNamespaceRequest, DetachContextFromGroupRequest, GroupContextEntry, GroupInfo, CreateGroupInvitationRequest, GroupMember, MetadataRecord, GroupUpgradeStatusResponseData, JoinGroupRequest, JoinNamespaceRequest, MigrationStatus, MigrationStatusRollup, MemberMigrationStatusEntry, MigrateMyEntriesSummary, Namespace, SubgroupEntry, NamespaceIdentity, NestGroupRequest, RegisterGroupSigningKeyRequest, RemoveGroupMembersRequest, ResyncContextRequest, RetryGroupUpgradeRequest, SetDefaultCapabilitiesRequest, SetSubgroupVisibilityRequest, SetTeeAdmissionPolicyRequest, SseEventData, SyncGroupRequest, UnnestGroupRequest, UpdateGroupSettingsRequest, UpdateMemberRoleRequest, UpgradeGroupRequest, TokenData } from '@calimero-network/mero-js';
|
|
6
6
|
export * from '@calimero-network/mero-js';
|
|
7
7
|
export { CAPABILITIES, MetadataRecord, hasCap, withCap, withoutCap } from '@calimero-network/mero-js';
|
|
8
8
|
|
|
@@ -129,13 +129,32 @@ interface MeroProviderConfig {
|
|
|
129
129
|
registryUrl?: string;
|
|
130
130
|
/** Request timeout in milliseconds */
|
|
131
131
|
timeoutMs?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Origins the OAuth callback is allowed to authenticate against. Defends
|
|
134
|
+
* against a malicious `node_url` in the callback URL (token exfiltration):
|
|
135
|
+
* the node login was initiated with is always trusted, and this allowlist
|
|
136
|
+
* additionally permits direct-callback entry to known nodes. When neither is
|
|
137
|
+
* available (no initiated node and no allowlist) a callback node_url is
|
|
138
|
+
* rejected and a security error is logged.
|
|
139
|
+
*/
|
|
140
|
+
allowedNodeUrls?: string[];
|
|
141
|
+
/**
|
|
142
|
+
* Token store for access/refresh tokens. Defaults to a localStorage-backed
|
|
143
|
+
* store that persists across reloads.
|
|
144
|
+
*
|
|
145
|
+
* SECURITY: localStorage tokens — including the refresh token — are readable
|
|
146
|
+
* by any script on the page, so an XSS bug can exfiltrate them. For sensitive
|
|
147
|
+
* deployments pass a `MemoryTokenStore` (session-only) or a store backed by an
|
|
148
|
+
* HttpOnly cookie set by your auth service.
|
|
149
|
+
*/
|
|
150
|
+
tokenStore?: TokenStore;
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
declare const MeroContext: React__default.Context<MeroContextValue | null>;
|
|
135
154
|
interface MeroProviderProps extends MeroProviderConfig {
|
|
136
155
|
children: React__default.ReactNode;
|
|
137
156
|
}
|
|
138
|
-
declare function MeroProvider({ children, mode, packageName, packageVersion, registryUrl, timeoutMs, }: MeroProviderProps): react_jsx_runtime.JSX.Element;
|
|
157
|
+
declare function MeroProvider({ children, mode, packageName, packageVersion, registryUrl, timeoutMs, allowedNodeUrls, tokenStore: tokenStoreProp, }: MeroProviderProps): react_jsx_runtime.JSX.Element;
|
|
139
158
|
declare function useMero(): MeroContextValue;
|
|
140
159
|
|
|
141
160
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createContext, useState, useRef, useCallback, useEffect,
|
|
2
|
-
import { parseAuthCallback, MeroJs, buildAuthLoginUrl, compareSemver
|
|
1
|
+
import { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext } from 'react';
|
|
2
|
+
import { LocalStorageTokenStore, parseAuthCallback, MeroJs, buildAuthLoginUrl, compareSemver } from '@calimero-network/mero-js';
|
|
3
3
|
export * from '@calimero-network/mero-js';
|
|
4
4
|
export { CAPABILITIES, hasCap, withCap, withoutCap } from '@calimero-network/mero-js';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -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 = createContext(null);
|
|
177
199
|
var isBrowser = typeof window !== "undefined";
|
|
178
|
-
var _tokenStore = null;
|
|
179
|
-
function getTokenStore() {
|
|
180
|
-
if (!_tokenStore) {
|
|
181
|
-
_tokenStore = new 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 = useMemo(
|
|
238
|
+
() => tokenStoreProp ?? new LocalStorageTokenStore(),
|
|
239
|
+
[tokenStoreProp]
|
|
240
|
+
);
|
|
220
241
|
const [mero, setMero] = useState(null);
|
|
221
242
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
222
243
|
const [isOnline, setIsOnline] = useState(true);
|
|
@@ -239,13 +260,13 @@ function MeroProvider({
|
|
|
239
260
|
}
|
|
240
261
|
const instance = new MeroJs({
|
|
241
262
|
baseUrl: url,
|
|
242
|
-
tokenStore
|
|
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 = 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
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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 =
|
|
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 (
|
|
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
|
useEffect(() => {
|
|
348
381
|
if (!isAuthenticated || !meroRef.current) return;
|
|
349
382
|
let active = true;
|
|
@@ -2187,7 +2220,9 @@ function useGroupUpgradeStatus(groupId) {
|
|
|
2187
2220
|
const [loading, setLoading] = useState(false);
|
|
2188
2221
|
const [error, setError] = useState(null);
|
|
2189
2222
|
const mountedRef = useMountedRef();
|
|
2223
|
+
const reqRef = useRef(0);
|
|
2190
2224
|
const refetch = useCallback(async () => {
|
|
2225
|
+
const seq = ++reqRef.current;
|
|
2191
2226
|
if (!mero || !groupId) {
|
|
2192
2227
|
if (mountedRef.current) {
|
|
2193
2228
|
setUpgradeStatus(null);
|
|
@@ -2202,20 +2237,25 @@ function useGroupUpgradeStatus(groupId) {
|
|
|
2202
2237
|
}
|
|
2203
2238
|
try {
|
|
2204
2239
|
const result = await mero.admin.getGroupUpgradeStatus(groupId);
|
|
2205
|
-
if (mountedRef.current) {
|
|
2240
|
+
if (mountedRef.current && seq === reqRef.current) {
|
|
2206
2241
|
setUpgradeStatus(result);
|
|
2207
2242
|
}
|
|
2208
2243
|
} catch (err) {
|
|
2209
2244
|
const errorValue = toError(err);
|
|
2210
|
-
if (mountedRef.current) {
|
|
2245
|
+
if (mountedRef.current && seq === reqRef.current) {
|
|
2211
2246
|
setError(errorValue);
|
|
2212
2247
|
}
|
|
2213
2248
|
} finally {
|
|
2214
|
-
if (mountedRef.current) {
|
|
2249
|
+
if (mountedRef.current && seq === reqRef.current) {
|
|
2215
2250
|
setLoading(false);
|
|
2216
2251
|
}
|
|
2217
2252
|
}
|
|
2218
2253
|
}, [groupId, mero, mountedRef]);
|
|
2254
|
+
useEffect(() => {
|
|
2255
|
+
reqRef.current += 1;
|
|
2256
|
+
setUpgradeStatus(null);
|
|
2257
|
+
setError(null);
|
|
2258
|
+
}, [groupId]);
|
|
2219
2259
|
useEffect(() => {
|
|
2220
2260
|
void refetch();
|
|
2221
2261
|
}, [refetch]);
|
|
@@ -2500,6 +2540,11 @@ function useMyAuthoredMigration(contextId) {
|
|
|
2500
2540
|
} catch {
|
|
2501
2541
|
}
|
|
2502
2542
|
}, [mero, contextId, mountedRef]);
|
|
2543
|
+
useEffect(() => {
|
|
2544
|
+
reqRef.current += 1;
|
|
2545
|
+
setPendingCount(0);
|
|
2546
|
+
setSummary(null);
|
|
2547
|
+
}, [contextId]);
|
|
2503
2548
|
useEffect(() => {
|
|
2504
2549
|
void refresh();
|
|
2505
2550
|
}, [refresh]);
|