@rebasepro/auth 0.2.1 → 0.2.4

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 {
@@ -231,7 +227,8 @@
231
227
  photoURL: userInfo.photoURL || null,
232
228
  providerId: "custom",
233
229
  isAnonymous: false,
234
- roles: userInfo.roles || []
230
+ roles: userInfo.roles || [],
231
+ metadata: userInfo.metadata
235
232
  };
236
233
  }
237
234
  function saveAuthToStorage(tokens, user) {
@@ -278,12 +275,11 @@
278
275
  const refreshPromiseRef = react.useRef(null);
279
276
  const isMountedRef = react.useRef(true);
280
277
  react.useEffect(() => {
281
- if (client) {
282
- setApiUrl(client.baseUrl);
283
- } else if (apiUrl) {
284
- setApiUrl(apiUrl);
278
+ const url = client?.baseUrl || apiUrl;
279
+ if (url) {
280
+ setApiUrl(url);
285
281
  }
286
- }, [client, apiUrl]);
282
+ }, [client, client?.baseUrl, apiUrl]);
287
283
  const clearError = react.useCallback(() => {
288
284
  setAuthProviderError(null);
289
285
  }, []);
@@ -399,7 +395,7 @@
399
395
  }, [initialLoading, refreshAccessToken$1, clearSessionAndSignOut]);
400
396
  react.useEffect(() => {
401
397
  if (client) {
402
- client.setAuthTokenGetter(async () => {
398
+ client.setAuthTokenGetter?.(async () => {
403
399
  try {
404
400
  return await getAuthToken();
405
401
  } catch {
@@ -675,6 +671,10 @@
675
671
  }
676
672
  } catch (meError) {
677
673
  if (!isMountedRef.current) return;
674
+ if (meError instanceof AuthApiError && (meError.code === "NOT_FOUND" || meError.code === "UNAUTHORIZED")) {
675
+ clearSessionAndSignOut();
676
+ return;
677
+ }
678
678
  userToSet = convertToUser(stored.user);
679
679
  }
680
680
  if (!isMountedRef.current) return;
@@ -774,10 +774,10 @@
774
774
  emailPasswordLogin: true,
775
775
  googleLogin: !!props.googleClientId,
776
776
  registration: authConfig?.registrationEnabled ?? false,
777
- passwordReset: true,
777
+ passwordReset: authConfig?.passwordReset ?? false,
778
778
  sessionManagement: true,
779
779
  profileUpdate: true,
780
- emailVerification: false,
780
+ emailVerification: authConfig?.emailVerification ?? false,
781
781
  enabledProviders: authConfig?.enabledProviders ?? []
782
782
  }
783
783
  };
@@ -798,18 +798,34 @@
798
798
  return {
799
799
  id: apiRole.id,
800
800
  name: apiRole.name,
801
- isAdmin: apiRole.isAdmin ?? false,
802
- config: apiRole.config ?? void 0
801
+ isAdmin: apiRole.isAdmin ?? false
803
802
  };
804
803
  }
805
804
  function useBackendUserManagement(config) {
806
805
  const { client, apiUrl, getAuthToken, currentUser } = config;
807
- const [users, setUsers] = react.useState([]);
806
+ const [userCache, setUserCache] = react.useState(/* @__PURE__ */ new Map());
807
+ const [hasAdminUsers, setHasAdminUsers] = react.useState(false);
808
808
  const [roles, setRoles] = react.useState([]);
809
- 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
+ });
810
816
  const [usersError, setUsersError] = react.useState();
811
817
  const [rolesError, setRolesError] = react.useState();
812
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
+ }, []);
813
829
  const apiRequestRef = react.useRef(null);
814
830
  const apiRequest = react.useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
815
831
  let lastError = null;
@@ -820,7 +836,7 @@
820
836
  throw error;
821
837
  }
822
838
  try {
823
- const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
839
+ const token = getAuthToken ? await getAuthToken() : client?.resolveToken ? await client.resolveToken() : null;
824
840
  const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
825
841
  const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
826
842
  method,
@@ -890,26 +906,29 @@
890
906
  setRolesError(error instanceof Error ? error : new Error(String(error)));
891
907
  }
892
908
  }, [apiRequest]);
893
- const loadUsers = react.useCallback(async (signal) => {
909
+ const checkAdminExists = react.useCallback(async (signal) => {
894
910
  try {
895
- const data = await apiRequest("/users", "GET", void 0, 6, signal);
896
- const allUsers = data.users.map((u) => convertUser(u));
897
- 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
+ }
898
917
  setUsersError(void 0);
899
918
  } catch (error) {
900
919
  if (error instanceof Error && error.name === "AbortError") return;
901
- console.error("Failed to load users:", error);
920
+ console.error("Failed to check admin users:", error);
902
921
  setUsersError(error instanceof Error ? error : new Error(String(error)));
903
922
  }
904
- }, [apiRequest]);
923
+ }, [apiRequest, mergeIntoCache]);
905
924
  react.useEffect(() => {
906
925
  if (!currentUser) {
907
926
  setLoading(false);
908
927
  return;
909
928
  }
910
- const userRoles = currentUser.roles ?? [];
911
- const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
912
- if (!isUserAdmin) {
929
+ const userRoles2 = currentUser.roles ?? [];
930
+ const isUserAdmin2 = userRoles2.some((r) => r === "admin" || r === "schema-admin");
931
+ if (!isUserAdmin2) {
913
932
  setLoading(false);
914
933
  return;
915
934
  }
@@ -938,13 +957,16 @@
938
957
  }
939
958
  if (!abortController.signal.aborted) {
940
959
  try {
941
- const data = await request("/users", "GET", void 0, 6, abortController.signal);
942
- const allUsers = data.users.map((u) => convertUser(u));
943
- 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
+ }
944
966
  setUsersError(void 0);
945
967
  } catch (error) {
946
968
  if (error instanceof Error && error.name === "AbortError") return;
947
- console.error("Failed to load users:", error);
969
+ console.error("Failed to check admin users:", error);
948
970
  setUsersError(error instanceof Error ? error : new Error(String(error)));
949
971
  }
950
972
  }
@@ -968,34 +990,24 @@
968
990
  if (options.roleId) params.set("role", options.roleId);
969
991
  const qs = params.toString();
970
992
  const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
993
+ const converted = data.users.map((u) => convertUser(u));
994
+ mergeIntoCache(converted);
971
995
  return {
972
- users: data.users.map((u) => convertUser(u)),
996
+ users: converted,
973
997
  total: data.total
974
998
  };
975
- }, [apiRequest]);
999
+ }, [apiRequest, mergeIntoCache]);
976
1000
  const saveUser = react.useCallback(async (user) => {
977
1001
  const roleIds = user.roles ?? [];
978
- const existingUser = users.find((u) => u.uid === user.uid);
979
- if (existingUser) {
980
- const data = await apiRequest(`/users/${user.uid}`, "PUT", {
981
- email: user.email,
982
- displayName: user.displayName,
983
- roles: roleIds
984
- });
985
- const updated = convertUser(data.user);
986
- setUsers((prev) => prev.map((u) => u.uid === updated.uid ? updated : u));
987
- return updated;
988
- } else {
989
- const data = await apiRequest("/users", "POST", {
990
- email: user.email,
991
- displayName: user.displayName,
992
- roles: roleIds
993
- });
994
- const created = convertUser(data.user);
995
- setUsers((prev) => [...prev, created]);
996
- return created;
997
- }
998
- }, [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]);
999
1011
  const createUser = react.useCallback(async (user) => {
1000
1012
  const roleIds = user.roles ?? [];
1001
1013
  const data = await apiRequest("/users", "POST", {
@@ -1004,34 +1016,37 @@
1004
1016
  roles: roleIds
1005
1017
  });
1006
1018
  const created = convertUser(data.user);
1007
- setUsers((prev) => [...prev, created]);
1019
+ mergeIntoCache([created]);
1008
1020
  return {
1009
1021
  user: created,
1010
1022
  invitationSent: data.invitationSent ?? false,
1011
1023
  temporaryPassword: data.temporaryPassword
1012
1024
  };
1013
- }, [apiRequest, roles]);
1025
+ }, [apiRequest, mergeIntoCache]);
1014
1026
  const resetPassword2 = react.useCallback(async (user) => {
1015
1027
  const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
1016
1028
  const updatedUser = convertUser(data.user);
1017
- setUsers((prev) => prev.map((u) => u.uid === updatedUser.uid ? updatedUser : u));
1029
+ mergeIntoCache([updatedUser]);
1018
1030
  return {
1019
1031
  user: updatedUser,
1020
1032
  invitationSent: data.invitationSent ?? false,
1021
1033
  temporaryPassword: data.temporaryPassword
1022
1034
  };
1023
- }, [apiRequest]);
1035
+ }, [apiRequest, mergeIntoCache]);
1024
1036
  const deleteUser = react.useCallback(async (user) => {
1025
1037
  await apiRequest(`/users/${user.uid}`, "DELETE");
1026
- 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
+ });
1027
1043
  }, [apiRequest]);
1028
1044
  const saveRole = react.useCallback(async (role) => {
1029
1045
  const existingRole = roles.find((r) => r.id === role.id);
1030
1046
  if (existingRole) {
1031
1047
  const data = await apiRequest(`/roles/${role.id}`, "PUT", {
1032
1048
  name: role.name,
1033
- isAdmin: role.isAdmin,
1034
- config: role.config
1049
+ isAdmin: role.isAdmin
1035
1050
  });
1036
1051
  const updated = convertRole(data.role);
1037
1052
  setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
@@ -1039,8 +1054,7 @@
1039
1054
  const data = await apiRequest("/roles", "POST", {
1040
1055
  id: role.id,
1041
1056
  name: role.name,
1042
- isAdmin: role.isAdmin ?? false,
1043
- config: role.config
1057
+ isAdmin: role.isAdmin ?? false
1044
1058
  });
1045
1059
  const created = convertRole(data.role);
1046
1060
  setRoles((prev) => [...prev, created]);
@@ -1051,14 +1065,22 @@
1051
1065
  setRoles((prev) => prev.filter((r) => r.id !== role.id));
1052
1066
  }, [apiRequest]);
1053
1067
  const getUser = react.useCallback((uid) => {
1054
- return users.find((u) => u.uid === uid) ?? null;
1055
- }, [users]);
1068
+ return userCache.get(uid) ?? null;
1069
+ }, [userCache]);
1056
1070
  const defineRolesFor = react.useCallback(async (user) => {
1057
- const existingUser = users.find((u) => u.uid === user.uid || u.email === user.email);
1058
- 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
+ }
1059
1081
  const userRoleIds = existingUser.roles ?? [];
1060
1082
  return roles.filter((r) => userRoleIds.includes(r.id));
1061
- }, [users, roles]);
1083
+ }, [userCache, roles, apiRequest, mergeIntoCache]);
1062
1084
  const isAdmin = currentUser?.roles?.includes("admin") ?? false;
1063
1085
  const bootstrapAdmin = react.useCallback(async () => {
1064
1086
  try {
@@ -1066,15 +1088,17 @@
1066
1088
  const data = await apiRequest("/roles");
1067
1089
  const loadedRoles = data.roles.map(convertRole);
1068
1090
  setRoles(loadedRoles);
1069
- await loadUsers();
1091
+ await checkAdminExists();
1070
1092
  } catch (error) {
1071
1093
  console.error("Failed to bootstrap admin:", error);
1072
1094
  throw error;
1073
1095
  }
1074
- }, [apiRequest, loadUsers]);
1096
+ }, [apiRequest, checkAdminExists]);
1097
+ const users = Array.from(userCache.values());
1075
1098
  return {
1076
- loading,
1099
+ loading: effectiveLoading,
1077
1100
  users,
1101
+ hasAdminUsers,
1078
1102
  saveUser,
1079
1103
  createUser,
1080
1104
  resetPassword: resetPassword2,
@@ -1084,7 +1108,6 @@
1084
1108
  deleteRole,
1085
1109
  isAdmin,
1086
1110
  allowDefaultRolesCreation: isAdmin,
1087
- includeCollectionConfigPermissions: true,
1088
1111
  defineRolesFor,
1089
1112
  getUser,
1090
1113
  searchUsers,
@@ -1094,7 +1117,6 @@
1094
1117
  };
1095
1118
  }
1096
1119
  exports2.AuthApiError = AuthApiError;
1097
- exports2.clearAuthConfigCache = clearAuthConfigCache;
1098
1120
  exports2.fetchAuthConfig = fetchAuthConfig;
1099
1121
  exports2.getApiUrl = getApiUrl;
1100
1122
  exports2.setApiUrl = setApiUrl;