@rebasepro/auth 0.2.3 → 0.3.0
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 +5 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +94 -73
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +94 -73
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/package.json +30 -4
- package/src/api.ts +5 -1
- package/src/hooks/useBackendUserManagement.ts +121 -86
- package/src/hooks/useRebaseAuthController.ts +12 -9
- package/src/index.ts +1 -1
- package/src/types.ts +7 -1
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 {
|
|
@@ -676,6 +671,10 @@
|
|
|
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
|
};
|
|
@@ -799,18 +798,34 @@
|
|
|
799
798
|
return {
|
|
800
799
|
id: apiRole.id,
|
|
801
800
|
name: apiRole.name,
|
|
802
|
-
isAdmin: apiRole.isAdmin ?? false
|
|
803
|
-
config: apiRole.config ?? void 0
|
|
801
|
+
isAdmin: apiRole.isAdmin ?? false
|
|
804
802
|
};
|
|
805
803
|
}
|
|
806
804
|
function useBackendUserManagement(config) {
|
|
807
805
|
const { client, apiUrl, getAuthToken, currentUser } = config;
|
|
808
|
-
const [
|
|
806
|
+
const [userCache, setUserCache] = react.useState(/* @__PURE__ */ new Map());
|
|
807
|
+
const [hasAdminUsers, setHasAdminUsers] = react.useState(false);
|
|
809
808
|
const [roles, setRoles] = react.useState([]);
|
|
810
|
-
const
|
|
809
|
+
const userRoles = currentUser?.roles ?? [];
|
|
810
|
+
const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
|
|
811
|
+
const [loading, setLoading] = react.useState(() => {
|
|
812
|
+
if (!currentUser) return false;
|
|
813
|
+
if (!isUserAdmin) return false;
|
|
814
|
+
return true;
|
|
815
|
+
});
|
|
811
816
|
const [usersError, setUsersError] = react.useState();
|
|
812
817
|
const [rolesError, setRolesError] = react.useState();
|
|
813
818
|
const lastLoadedUidRef = react.useRef(null);
|
|
819
|
+
const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
|
|
820
|
+
const mergeIntoCache = react.useCallback((incoming) => {
|
|
821
|
+
setUserCache((prev) => {
|
|
822
|
+
const next = new Map(prev);
|
|
823
|
+
for (const u of incoming) {
|
|
824
|
+
next.set(u.uid, u);
|
|
825
|
+
}
|
|
826
|
+
return next;
|
|
827
|
+
});
|
|
828
|
+
}, []);
|
|
814
829
|
const apiRequestRef = react.useRef(null);
|
|
815
830
|
const apiRequest = react.useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
816
831
|
let lastError = null;
|
|
@@ -821,7 +836,7 @@
|
|
|
821
836
|
throw error;
|
|
822
837
|
}
|
|
823
838
|
try {
|
|
824
|
-
const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
|
|
839
|
+
const token = getAuthToken ? await getAuthToken() : client?.resolveToken ? await client.resolveToken() : null;
|
|
825
840
|
const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
|
|
826
841
|
const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
|
|
827
842
|
method,
|
|
@@ -891,26 +906,29 @@
|
|
|
891
906
|
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
892
907
|
}
|
|
893
908
|
}, [apiRequest]);
|
|
894
|
-
const
|
|
909
|
+
const checkAdminExists = react.useCallback(async (signal) => {
|
|
895
910
|
try {
|
|
896
|
-
const data = await apiRequest("/users", "GET", void 0, 6, signal);
|
|
897
|
-
const
|
|
898
|
-
|
|
911
|
+
const data = await apiRequest("/users?role=admin&limit=1", "GET", void 0, 6, signal);
|
|
912
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
913
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
914
|
+
if (adminUsers.length > 0) {
|
|
915
|
+
mergeIntoCache(adminUsers);
|
|
916
|
+
}
|
|
899
917
|
setUsersError(void 0);
|
|
900
918
|
} catch (error) {
|
|
901
919
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
902
|
-
console.error("Failed to
|
|
920
|
+
console.error("Failed to check admin users:", error);
|
|
903
921
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
904
922
|
}
|
|
905
|
-
}, [apiRequest]);
|
|
923
|
+
}, [apiRequest, mergeIntoCache]);
|
|
906
924
|
react.useEffect(() => {
|
|
907
925
|
if (!currentUser) {
|
|
908
926
|
setLoading(false);
|
|
909
927
|
return;
|
|
910
928
|
}
|
|
911
|
-
const
|
|
912
|
-
const
|
|
913
|
-
if (!
|
|
929
|
+
const userRoles2 = currentUser.roles ?? [];
|
|
930
|
+
const isUserAdmin2 = userRoles2.some((r) => r === "admin" || r === "schema-admin");
|
|
931
|
+
if (!isUserAdmin2) {
|
|
914
932
|
setLoading(false);
|
|
915
933
|
return;
|
|
916
934
|
}
|
|
@@ -939,13 +957,16 @@
|
|
|
939
957
|
}
|
|
940
958
|
if (!abortController.signal.aborted) {
|
|
941
959
|
try {
|
|
942
|
-
const data = await request("/users", "GET", void 0, 6, abortController.signal);
|
|
943
|
-
const
|
|
944
|
-
|
|
960
|
+
const data = await request("/users?role=admin&limit=1", "GET", void 0, 6, abortController.signal);
|
|
961
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
962
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
963
|
+
if (adminUsers.length > 0) {
|
|
964
|
+
mergeIntoCache(adminUsers);
|
|
965
|
+
}
|
|
945
966
|
setUsersError(void 0);
|
|
946
967
|
} catch (error) {
|
|
947
968
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
948
|
-
console.error("Failed to
|
|
969
|
+
console.error("Failed to check admin users:", error);
|
|
949
970
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
950
971
|
}
|
|
951
972
|
}
|
|
@@ -969,34 +990,24 @@
|
|
|
969
990
|
if (options.roleId) params.set("role", options.roleId);
|
|
970
991
|
const qs = params.toString();
|
|
971
992
|
const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
|
|
993
|
+
const converted = data.users.map((u) => convertUser(u));
|
|
994
|
+
mergeIntoCache(converted);
|
|
972
995
|
return {
|
|
973
|
-
users:
|
|
996
|
+
users: converted,
|
|
974
997
|
total: data.total
|
|
975
998
|
};
|
|
976
|
-
}, [apiRequest]);
|
|
999
|
+
}, [apiRequest, mergeIntoCache]);
|
|
977
1000
|
const saveUser = react.useCallback(async (user) => {
|
|
978
1001
|
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]);
|
|
1002
|
+
const data = await apiRequest(`/users/${user.uid}`, "PUT", {
|
|
1003
|
+
email: user.email,
|
|
1004
|
+
displayName: user.displayName,
|
|
1005
|
+
roles: roleIds
|
|
1006
|
+
});
|
|
1007
|
+
const updated = convertUser(data.user);
|
|
1008
|
+
mergeIntoCache([updated]);
|
|
1009
|
+
return updated;
|
|
1010
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1000
1011
|
const createUser = react.useCallback(async (user) => {
|
|
1001
1012
|
const roleIds = user.roles ?? [];
|
|
1002
1013
|
const data = await apiRequest("/users", "POST", {
|
|
@@ -1005,34 +1016,37 @@
|
|
|
1005
1016
|
roles: roleIds
|
|
1006
1017
|
});
|
|
1007
1018
|
const created = convertUser(data.user);
|
|
1008
|
-
|
|
1019
|
+
mergeIntoCache([created]);
|
|
1009
1020
|
return {
|
|
1010
1021
|
user: created,
|
|
1011
1022
|
invitationSent: data.invitationSent ?? false,
|
|
1012
1023
|
temporaryPassword: data.temporaryPassword
|
|
1013
1024
|
};
|
|
1014
|
-
}, [apiRequest,
|
|
1025
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1015
1026
|
const resetPassword2 = react.useCallback(async (user) => {
|
|
1016
1027
|
const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
|
|
1017
1028
|
const updatedUser = convertUser(data.user);
|
|
1018
|
-
|
|
1029
|
+
mergeIntoCache([updatedUser]);
|
|
1019
1030
|
return {
|
|
1020
1031
|
user: updatedUser,
|
|
1021
1032
|
invitationSent: data.invitationSent ?? false,
|
|
1022
1033
|
temporaryPassword: data.temporaryPassword
|
|
1023
1034
|
};
|
|
1024
|
-
}, [apiRequest]);
|
|
1035
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1025
1036
|
const deleteUser = react.useCallback(async (user) => {
|
|
1026
1037
|
await apiRequest(`/users/${user.uid}`, "DELETE");
|
|
1027
|
-
|
|
1038
|
+
setUserCache((prev) => {
|
|
1039
|
+
const next = new Map(prev);
|
|
1040
|
+
next.delete(user.uid);
|
|
1041
|
+
return next;
|
|
1042
|
+
});
|
|
1028
1043
|
}, [apiRequest]);
|
|
1029
1044
|
const saveRole = react.useCallback(async (role) => {
|
|
1030
1045
|
const existingRole = roles.find((r) => r.id === role.id);
|
|
1031
1046
|
if (existingRole) {
|
|
1032
1047
|
const data = await apiRequest(`/roles/${role.id}`, "PUT", {
|
|
1033
1048
|
name: role.name,
|
|
1034
|
-
isAdmin: role.isAdmin
|
|
1035
|
-
config: role.config
|
|
1049
|
+
isAdmin: role.isAdmin
|
|
1036
1050
|
});
|
|
1037
1051
|
const updated = convertRole(data.role);
|
|
1038
1052
|
setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
|
|
@@ -1040,8 +1054,7 @@
|
|
|
1040
1054
|
const data = await apiRequest("/roles", "POST", {
|
|
1041
1055
|
id: role.id,
|
|
1042
1056
|
name: role.name,
|
|
1043
|
-
isAdmin: role.isAdmin ?? false
|
|
1044
|
-
config: role.config
|
|
1057
|
+
isAdmin: role.isAdmin ?? false
|
|
1045
1058
|
});
|
|
1046
1059
|
const created = convertRole(data.role);
|
|
1047
1060
|
setRoles((prev) => [...prev, created]);
|
|
@@ -1052,14 +1065,22 @@
|
|
|
1052
1065
|
setRoles((prev) => prev.filter((r) => r.id !== role.id));
|
|
1053
1066
|
}, [apiRequest]);
|
|
1054
1067
|
const getUser = react.useCallback((uid) => {
|
|
1055
|
-
return
|
|
1056
|
-
}, [
|
|
1068
|
+
return userCache.get(uid) ?? null;
|
|
1069
|
+
}, [userCache]);
|
|
1057
1070
|
const defineRolesFor = react.useCallback(async (user) => {
|
|
1058
|
-
|
|
1059
|
-
if (!existingUser)
|
|
1071
|
+
let existingUser = userCache.get(user.uid) ?? Array.from(userCache.values()).find((u) => u.email === user.email);
|
|
1072
|
+
if (!existingUser) {
|
|
1073
|
+
try {
|
|
1074
|
+
const data = await apiRequest(`/users/${user.uid}`, "GET");
|
|
1075
|
+
existingUser = convertUser(data.user);
|
|
1076
|
+
mergeIntoCache([existingUser]);
|
|
1077
|
+
} catch {
|
|
1078
|
+
return void 0;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1060
1081
|
const userRoleIds = existingUser.roles ?? [];
|
|
1061
1082
|
return roles.filter((r) => userRoleIds.includes(r.id));
|
|
1062
|
-
}, [
|
|
1083
|
+
}, [userCache, roles, apiRequest, mergeIntoCache]);
|
|
1063
1084
|
const isAdmin = currentUser?.roles?.includes("admin") ?? false;
|
|
1064
1085
|
const bootstrapAdmin = react.useCallback(async () => {
|
|
1065
1086
|
try {
|
|
@@ -1067,15 +1088,17 @@
|
|
|
1067
1088
|
const data = await apiRequest("/roles");
|
|
1068
1089
|
const loadedRoles = data.roles.map(convertRole);
|
|
1069
1090
|
setRoles(loadedRoles);
|
|
1070
|
-
await
|
|
1091
|
+
await checkAdminExists();
|
|
1071
1092
|
} catch (error) {
|
|
1072
1093
|
console.error("Failed to bootstrap admin:", error);
|
|
1073
1094
|
throw error;
|
|
1074
1095
|
}
|
|
1075
|
-
}, [apiRequest,
|
|
1096
|
+
}, [apiRequest, checkAdminExists]);
|
|
1097
|
+
const users = Array.from(userCache.values());
|
|
1076
1098
|
return {
|
|
1077
|
-
loading,
|
|
1099
|
+
loading: effectiveLoading,
|
|
1078
1100
|
users,
|
|
1101
|
+
hasAdminUsers,
|
|
1079
1102
|
saveUser,
|
|
1080
1103
|
createUser,
|
|
1081
1104
|
resetPassword: resetPassword2,
|
|
@@ -1085,7 +1108,6 @@
|
|
|
1085
1108
|
deleteRole,
|
|
1086
1109
|
isAdmin,
|
|
1087
1110
|
allowDefaultRolesCreation: isAdmin,
|
|
1088
|
-
includeCollectionConfigPermissions: true,
|
|
1089
1111
|
defineRolesFor,
|
|
1090
1112
|
getUser,
|
|
1091
1113
|
searchUsers,
|
|
@@ -1095,7 +1117,6 @@
|
|
|
1095
1117
|
};
|
|
1096
1118
|
}
|
|
1097
1119
|
exports2.AuthApiError = AuthApiError;
|
|
1098
|
-
exports2.clearAuthConfigCache = clearAuthConfigCache;
|
|
1099
1120
|
exports2.fetchAuthConfig = fetchAuthConfig;
|
|
1100
1121
|
exports2.getApiUrl = getApiUrl;
|
|
1101
1122
|
exports2.setApiUrl = setApiUrl;
|