@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/index.umd.js CHANGED
@@ -217,11 +217,7 @@
217
217
  authConfigInflight = null;
218
218
  }
219
219
  }
220
- function clearAuthConfigCache() {
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
- if (client) {
283
- setApiUrl(client.baseUrl);
284
- } else if (apiUrl) {
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: true,
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 [users, setUsers] = react.useState([]);
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 [loading, setLoading] = react.useState(true);
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 loadUsers = react.useCallback(async (signal) => {
909
+ const checkAdminExists = react.useCallback(async (signal) => {
895
910
  try {
896
- const data = await apiRequest("/users", "GET", void 0, 6, signal);
897
- const allUsers = data.users.map((u) => convertUser(u));
898
- setUsers(allUsers);
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 load users:", error);
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 userRoles = currentUser.roles ?? [];
912
- const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
913
- if (!isUserAdmin) {
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 allUsers = data.users.map((u) => convertUser(u));
944
- setUsers(allUsers);
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 load users:", error);
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: data.users.map((u) => convertUser(u)),
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 existingUser = users.find((u) => u.uid === user.uid);
980
- if (existingUser) {
981
- const data = await apiRequest(`/users/${user.uid}`, "PUT", {
982
- email: user.email,
983
- displayName: user.displayName,
984
- roles: roleIds
985
- });
986
- const updated = convertUser(data.user);
987
- setUsers((prev) => prev.map((u) => u.uid === updated.uid ? updated : u));
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
- setUsers((prev) => [...prev, created]);
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, roles]);
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
- setUsers((prev) => prev.map((u) => u.uid === updatedUser.uid ? updatedUser : u));
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
- setUsers((prev) => prev.filter((u) => u.uid !== user.uid));
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 users.find((u) => u.uid === uid) ?? null;
1056
- }, [users]);
1068
+ return userCache.get(uid) ?? null;
1069
+ }, [userCache]);
1057
1070
  const defineRolesFor = react.useCallback(async (user) => {
1058
- const existingUser = users.find((u) => u.uid === user.uid || u.email === user.email);
1059
- if (!existingUser) return void 0;
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
- }, [users, roles]);
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 loadUsers();
1091
+ await checkAdminExists();
1071
1092
  } catch (error) {
1072
1093
  console.error("Failed to bootstrap admin:", error);
1073
1094
  throw error;
1074
1095
  }
1075
- }, [apiRequest, loadUsers]);
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;