@rebasepro/auth 0.2.3 → 0.2.5
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/api.d.ts +5 -1
- package/dist/hooks/useBackendUserManagement.d.ts +7 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +96 -140
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +96 -140
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +11 -3
- package/package.json +30 -4
- package/src/api.ts +5 -1
- package/src/hooks/useBackendUserManagement.ts +113 -178
- package/src/hooks/useRebaseAuthController.ts +16 -13
- package/src/index.ts +1 -1
- package/src/types.ts +9 -3
package/dist/index.umd.js
CHANGED
|
@@ -217,11 +217,7 @@
|
|
|
217
217
|
authConfigInflight = null;
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
authConfigCached = null;
|
|
222
|
-
authConfigInflight = null;
|
|
223
|
-
}
|
|
224
|
-
const STORAGE_KEY = "rebase_auth";
|
|
220
|
+
const STORAGE_KEY = "rebase_react_auth";
|
|
225
221
|
const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1e3;
|
|
226
222
|
function convertToUser(userInfo) {
|
|
227
223
|
return {
|
|
@@ -279,12 +275,11 @@
|
|
|
279
275
|
const refreshPromiseRef = react.useRef(null);
|
|
280
276
|
const isMountedRef = react.useRef(true);
|
|
281
277
|
react.useEffect(() => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
setApiUrl(apiUrl);
|
|
278
|
+
const url = client?.baseUrl || apiUrl;
|
|
279
|
+
if (url) {
|
|
280
|
+
setApiUrl(url);
|
|
286
281
|
}
|
|
287
|
-
}, [client, apiUrl]);
|
|
282
|
+
}, [client, client?.baseUrl, apiUrl]);
|
|
288
283
|
const clearError = react.useCallback(() => {
|
|
289
284
|
setAuthProviderError(null);
|
|
290
285
|
}, []);
|
|
@@ -400,7 +395,7 @@
|
|
|
400
395
|
}, [initialLoading, refreshAccessToken$1, clearSessionAndSignOut]);
|
|
401
396
|
react.useEffect(() => {
|
|
402
397
|
if (client) {
|
|
403
|
-
client.setAuthTokenGetter(async () => {
|
|
398
|
+
client.setAuthTokenGetter?.(async () => {
|
|
404
399
|
try {
|
|
405
400
|
return await getAuthToken();
|
|
406
401
|
} catch {
|
|
@@ -433,7 +428,7 @@
|
|
|
433
428
|
if (defineRolesFor) {
|
|
434
429
|
const customRoles = await defineRolesFor(convertedUser);
|
|
435
430
|
if (customRoles) {
|
|
436
|
-
convertedUser = { ...convertedUser, roles: customRoles
|
|
431
|
+
convertedUser = { ...convertedUser, roles: customRoles };
|
|
437
432
|
}
|
|
438
433
|
}
|
|
439
434
|
saveAuthToStorage(tokens, userInfo);
|
|
@@ -564,7 +559,7 @@
|
|
|
564
559
|
if (customRoles) {
|
|
565
560
|
convertedUser = {
|
|
566
561
|
...convertedUser,
|
|
567
|
-
roles: customRoles
|
|
562
|
+
roles: customRoles
|
|
568
563
|
};
|
|
569
564
|
}
|
|
570
565
|
}
|
|
@@ -638,7 +633,7 @@
|
|
|
638
633
|
if (customRoles) {
|
|
639
634
|
userToSet = {
|
|
640
635
|
...userToSet,
|
|
641
|
-
roles: customRoles
|
|
636
|
+
roles: customRoles
|
|
642
637
|
};
|
|
643
638
|
}
|
|
644
639
|
}
|
|
@@ -670,12 +665,16 @@
|
|
|
670
665
|
if (customRoles) {
|
|
671
666
|
userToSet = {
|
|
672
667
|
...userToSet,
|
|
673
|
-
roles: customRoles
|
|
668
|
+
roles: customRoles
|
|
674
669
|
};
|
|
675
670
|
}
|
|
676
671
|
}
|
|
677
672
|
} catch (meError) {
|
|
678
673
|
if (!isMountedRef.current) return;
|
|
674
|
+
if (meError instanceof AuthApiError && (meError.code === "NOT_FOUND" || meError.code === "UNAUTHORIZED")) {
|
|
675
|
+
clearSessionAndSignOut();
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
679
678
|
userToSet = convertToUser(stored.user);
|
|
680
679
|
}
|
|
681
680
|
if (!isMountedRef.current) return;
|
|
@@ -775,10 +774,10 @@
|
|
|
775
774
|
emailPasswordLogin: true,
|
|
776
775
|
googleLogin: !!props.googleClientId,
|
|
777
776
|
registration: authConfig?.registrationEnabled ?? false,
|
|
778
|
-
passwordReset:
|
|
777
|
+
passwordReset: authConfig?.passwordReset ?? false,
|
|
779
778
|
sessionManagement: true,
|
|
780
779
|
profileUpdate: true,
|
|
781
|
-
emailVerification: false,
|
|
780
|
+
emailVerification: authConfig?.emailVerification ?? false,
|
|
782
781
|
enabledProviders: authConfig?.enabledProviders ?? []
|
|
783
782
|
}
|
|
784
783
|
};
|
|
@@ -795,22 +794,29 @@
|
|
|
795
794
|
createdAt: apiUser.createdAt ? new Date(apiUser.createdAt) : null
|
|
796
795
|
};
|
|
797
796
|
}
|
|
798
|
-
function convertRole(apiRole) {
|
|
799
|
-
return {
|
|
800
|
-
id: apiRole.id,
|
|
801
|
-
name: apiRole.name,
|
|
802
|
-
isAdmin: apiRole.isAdmin ?? false,
|
|
803
|
-
config: apiRole.config ?? void 0
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
797
|
function useBackendUserManagement(config) {
|
|
807
798
|
const { client, apiUrl, getAuthToken, currentUser } = config;
|
|
808
|
-
const [
|
|
809
|
-
const [
|
|
810
|
-
const
|
|
799
|
+
const [userCache, setUserCache] = react.useState(/* @__PURE__ */ new Map());
|
|
800
|
+
const [hasAdminUsers, setHasAdminUsers] = react.useState(false);
|
|
801
|
+
const userRoles = currentUser?.roles ?? [];
|
|
802
|
+
const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
|
|
803
|
+
const [loading, setLoading] = react.useState(() => {
|
|
804
|
+
if (!currentUser) return false;
|
|
805
|
+
if (!isUserAdmin) return false;
|
|
806
|
+
return true;
|
|
807
|
+
});
|
|
811
808
|
const [usersError, setUsersError] = react.useState();
|
|
812
|
-
const [rolesError, setRolesError] = react.useState();
|
|
813
809
|
const lastLoadedUidRef = react.useRef(null);
|
|
810
|
+
const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
|
|
811
|
+
const mergeIntoCache = react.useCallback((incoming) => {
|
|
812
|
+
setUserCache((prev) => {
|
|
813
|
+
const next = new Map(prev);
|
|
814
|
+
for (const u of incoming) {
|
|
815
|
+
next.set(u.uid, u);
|
|
816
|
+
}
|
|
817
|
+
return next;
|
|
818
|
+
});
|
|
819
|
+
}, []);
|
|
814
820
|
const apiRequestRef = react.useRef(null);
|
|
815
821
|
const apiRequest = react.useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
816
822
|
let lastError = null;
|
|
@@ -821,7 +827,7 @@
|
|
|
821
827
|
throw error;
|
|
822
828
|
}
|
|
823
829
|
try {
|
|
824
|
-
const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
|
|
830
|
+
const token = getAuthToken ? await getAuthToken() : client?.resolveToken ? await client.resolveToken() : null;
|
|
825
831
|
const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
|
|
826
832
|
const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
|
|
827
833
|
method,
|
|
@@ -880,37 +886,29 @@
|
|
|
880
886
|
throw lastError;
|
|
881
887
|
}, [apiUrl, getAuthToken]);
|
|
882
888
|
apiRequestRef.current = apiRequest;
|
|
883
|
-
react.useCallback(async (signal) => {
|
|
889
|
+
const checkAdminExists = react.useCallback(async (signal) => {
|
|
884
890
|
try {
|
|
885
|
-
const data = await apiRequest("/
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
892
|
-
}
|
|
893
|
-
}, [apiRequest]);
|
|
894
|
-
const loadUsers = react.useCallback(async (signal) => {
|
|
895
|
-
try {
|
|
896
|
-
const data = await apiRequest("/users", "GET", void 0, 6, signal);
|
|
897
|
-
const allUsers = data.users.map((u) => convertUser(u));
|
|
898
|
-
setUsers(allUsers);
|
|
891
|
+
const data = await apiRequest("/users?role=admin&limit=1", "GET", void 0, 6, signal);
|
|
892
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
893
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
894
|
+
if (adminUsers.length > 0) {
|
|
895
|
+
mergeIntoCache(adminUsers);
|
|
896
|
+
}
|
|
899
897
|
setUsersError(void 0);
|
|
900
898
|
} catch (error) {
|
|
901
899
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
902
|
-
console.error("Failed to
|
|
900
|
+
console.error("Failed to check admin users:", error);
|
|
903
901
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
904
902
|
}
|
|
905
|
-
}, [apiRequest]);
|
|
903
|
+
}, [apiRequest, mergeIntoCache]);
|
|
906
904
|
react.useEffect(() => {
|
|
907
905
|
if (!currentUser) {
|
|
908
906
|
setLoading(false);
|
|
909
907
|
return;
|
|
910
908
|
}
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
if (!
|
|
909
|
+
const userRoles2 = currentUser.roles ?? [];
|
|
910
|
+
const isUserAdmin2 = userRoles2.some((r) => r === "admin" || r === "schema-admin");
|
|
911
|
+
if (!isUserAdmin2) {
|
|
914
912
|
setLoading(false);
|
|
915
913
|
return;
|
|
916
914
|
}
|
|
@@ -922,30 +920,18 @@
|
|
|
922
920
|
const load = async () => {
|
|
923
921
|
setLoading(true);
|
|
924
922
|
const request = apiRequestRef.current;
|
|
925
|
-
try {
|
|
926
|
-
const data = await request("/roles", "GET", void 0, 6, abortController.signal);
|
|
927
|
-
setRoles(data.roles.map(convertRole));
|
|
928
|
-
setRolesError(void 0);
|
|
929
|
-
} catch (error) {
|
|
930
|
-
if (error instanceof Error && error.name === "AbortError") return;
|
|
931
|
-
console.error("Failed to load roles:", error);
|
|
932
|
-
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
933
|
-
const status = error.status;
|
|
934
|
-
if (status === 403 || status === 401) {
|
|
935
|
-
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
936
|
-
setLoading(false);
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
923
|
if (!abortController.signal.aborted) {
|
|
941
924
|
try {
|
|
942
|
-
const data = await request("/users", "GET", void 0, 6, abortController.signal);
|
|
943
|
-
const
|
|
944
|
-
|
|
925
|
+
const data = await request("/users?role=admin&limit=1", "GET", void 0, 6, abortController.signal);
|
|
926
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
927
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
928
|
+
if (adminUsers.length > 0) {
|
|
929
|
+
mergeIntoCache(adminUsers);
|
|
930
|
+
}
|
|
945
931
|
setUsersError(void 0);
|
|
946
932
|
} catch (error) {
|
|
947
933
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
948
|
-
console.error("Failed to
|
|
934
|
+
console.error("Failed to check admin users:", error);
|
|
949
935
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
950
936
|
}
|
|
951
937
|
}
|
|
@@ -969,34 +955,24 @@
|
|
|
969
955
|
if (options.roleId) params.set("role", options.roleId);
|
|
970
956
|
const qs = params.toString();
|
|
971
957
|
const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
|
|
958
|
+
const converted = data.users.map((u) => convertUser(u));
|
|
959
|
+
mergeIntoCache(converted);
|
|
972
960
|
return {
|
|
973
|
-
users:
|
|
961
|
+
users: converted,
|
|
974
962
|
total: data.total
|
|
975
963
|
};
|
|
976
|
-
}, [apiRequest]);
|
|
964
|
+
}, [apiRequest, mergeIntoCache]);
|
|
977
965
|
const saveUser = react.useCallback(async (user) => {
|
|
978
966
|
const roleIds = user.roles ?? [];
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
return updated;
|
|
989
|
-
} else {
|
|
990
|
-
const data = await apiRequest("/users", "POST", {
|
|
991
|
-
email: user.email,
|
|
992
|
-
displayName: user.displayName,
|
|
993
|
-
roles: roleIds
|
|
994
|
-
});
|
|
995
|
-
const created = convertUser(data.user);
|
|
996
|
-
setUsers((prev) => [...prev, created]);
|
|
997
|
-
return created;
|
|
998
|
-
}
|
|
999
|
-
}, [apiRequest, users, roles]);
|
|
967
|
+
const data = await apiRequest(`/users/${user.uid}`, "PUT", {
|
|
968
|
+
email: user.email,
|
|
969
|
+
displayName: user.displayName,
|
|
970
|
+
roles: roleIds
|
|
971
|
+
});
|
|
972
|
+
const updated = convertUser(data.user);
|
|
973
|
+
mergeIntoCache([updated]);
|
|
974
|
+
return updated;
|
|
975
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1000
976
|
const createUser = react.useCallback(async (user) => {
|
|
1001
977
|
const roleIds = user.roles ?? [];
|
|
1002
978
|
const data = await apiRequest("/users", "POST", {
|
|
@@ -1005,97 +981,77 @@
|
|
|
1005
981
|
roles: roleIds
|
|
1006
982
|
});
|
|
1007
983
|
const created = convertUser(data.user);
|
|
1008
|
-
|
|
984
|
+
mergeIntoCache([created]);
|
|
1009
985
|
return {
|
|
1010
986
|
user: created,
|
|
1011
987
|
invitationSent: data.invitationSent ?? false,
|
|
1012
988
|
temporaryPassword: data.temporaryPassword
|
|
1013
989
|
};
|
|
1014
|
-
}, [apiRequest,
|
|
990
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1015
991
|
const resetPassword2 = react.useCallback(async (user) => {
|
|
1016
992
|
const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
|
|
1017
993
|
const updatedUser = convertUser(data.user);
|
|
1018
|
-
|
|
994
|
+
mergeIntoCache([updatedUser]);
|
|
1019
995
|
return {
|
|
1020
996
|
user: updatedUser,
|
|
1021
997
|
invitationSent: data.invitationSent ?? false,
|
|
1022
998
|
temporaryPassword: data.temporaryPassword
|
|
1023
999
|
};
|
|
1024
|
-
}, [apiRequest]);
|
|
1000
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1025
1001
|
const deleteUser = react.useCallback(async (user) => {
|
|
1026
1002
|
await apiRequest(`/users/${user.uid}`, "DELETE");
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const data = await apiRequest(`/roles/${role.id}`, "PUT", {
|
|
1033
|
-
name: role.name,
|
|
1034
|
-
isAdmin: role.isAdmin,
|
|
1035
|
-
config: role.config
|
|
1036
|
-
});
|
|
1037
|
-
const updated = convertRole(data.role);
|
|
1038
|
-
setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
|
|
1039
|
-
} else {
|
|
1040
|
-
const data = await apiRequest("/roles", "POST", {
|
|
1041
|
-
id: role.id,
|
|
1042
|
-
name: role.name,
|
|
1043
|
-
isAdmin: role.isAdmin ?? false,
|
|
1044
|
-
config: role.config
|
|
1045
|
-
});
|
|
1046
|
-
const created = convertRole(data.role);
|
|
1047
|
-
setRoles((prev) => [...prev, created]);
|
|
1048
|
-
}
|
|
1049
|
-
}, [apiRequest, roles]);
|
|
1050
|
-
const deleteRole = react.useCallback(async (role) => {
|
|
1051
|
-
await apiRequest(`/roles/${role.id}`, "DELETE");
|
|
1052
|
-
setRoles((prev) => prev.filter((r) => r.id !== role.id));
|
|
1003
|
+
setUserCache((prev) => {
|
|
1004
|
+
const next = new Map(prev);
|
|
1005
|
+
next.delete(user.uid);
|
|
1006
|
+
return next;
|
|
1007
|
+
});
|
|
1053
1008
|
}, [apiRequest]);
|
|
1054
1009
|
const getUser = react.useCallback((uid) => {
|
|
1055
|
-
return
|
|
1056
|
-
}, [
|
|
1010
|
+
return userCache.get(uid) ?? null;
|
|
1011
|
+
}, [userCache]);
|
|
1057
1012
|
const defineRolesFor = react.useCallback(async (user) => {
|
|
1058
|
-
|
|
1059
|
-
if (!existingUser)
|
|
1013
|
+
let existingUser = userCache.get(user.uid) ?? Array.from(userCache.values()).find((u) => u.email === user.email);
|
|
1014
|
+
if (!existingUser) {
|
|
1015
|
+
try {
|
|
1016
|
+
const data = await apiRequest(`/users/${user.uid}`, "GET");
|
|
1017
|
+
existingUser = convertUser(data.user);
|
|
1018
|
+
mergeIntoCache([existingUser]);
|
|
1019
|
+
} catch {
|
|
1020
|
+
return void 0;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1060
1023
|
const userRoleIds = existingUser.roles ?? [];
|
|
1061
|
-
return
|
|
1062
|
-
}, [
|
|
1024
|
+
return userRoleIds;
|
|
1025
|
+
}, [userCache, apiRequest, mergeIntoCache]);
|
|
1063
1026
|
const isAdmin = currentUser?.roles?.includes("admin") ?? false;
|
|
1064
1027
|
const bootstrapAdmin = react.useCallback(async () => {
|
|
1065
1028
|
try {
|
|
1066
1029
|
await apiRequest("/bootstrap", "POST");
|
|
1067
|
-
|
|
1068
|
-
const loadedRoles = data.roles.map(convertRole);
|
|
1069
|
-
setRoles(loadedRoles);
|
|
1070
|
-
await loadUsers();
|
|
1030
|
+
await checkAdminExists();
|
|
1071
1031
|
} catch (error) {
|
|
1072
1032
|
console.error("Failed to bootstrap admin:", error);
|
|
1073
1033
|
throw error;
|
|
1074
1034
|
}
|
|
1075
|
-
}, [apiRequest,
|
|
1035
|
+
}, [apiRequest, checkAdminExists]);
|
|
1036
|
+
const users = Array.from(userCache.values());
|
|
1076
1037
|
return {
|
|
1077
|
-
loading,
|
|
1038
|
+
loading: effectiveLoading,
|
|
1078
1039
|
users,
|
|
1040
|
+
hasAdminUsers,
|
|
1079
1041
|
saveUser,
|
|
1080
1042
|
createUser,
|
|
1081
1043
|
resetPassword: resetPassword2,
|
|
1082
1044
|
deleteUser,
|
|
1083
|
-
roles,
|
|
1084
|
-
saveRole,
|
|
1085
|
-
deleteRole,
|
|
1086
1045
|
isAdmin,
|
|
1087
1046
|
allowDefaultRolesCreation: isAdmin,
|
|
1088
|
-
includeCollectionConfigPermissions: true,
|
|
1089
1047
|
defineRolesFor,
|
|
1090
1048
|
getUser,
|
|
1091
1049
|
searchUsers,
|
|
1092
1050
|
usersError,
|
|
1093
|
-
rolesError,
|
|
1094
1051
|
bootstrapAdmin
|
|
1095
1052
|
};
|
|
1096
1053
|
}
|
|
1097
1054
|
exports2.AuthApiError = AuthApiError;
|
|
1098
|
-
exports2.clearAuthConfigCache = clearAuthConfigCache;
|
|
1099
1055
|
exports2.fetchAuthConfig = fetchAuthConfig;
|
|
1100
1056
|
exports2.getApiUrl = getApiUrl;
|
|
1101
1057
|
exports2.setApiUrl = setApiUrl;
|