@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/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 {
@@ -433,7 +428,7 @@
433
428
  if (defineRolesFor) {
434
429
  const customRoles = await defineRolesFor(convertedUser);
435
430
  if (customRoles) {
436
- convertedUser = { ...convertedUser, roles: customRoles.map((r) => r.id) };
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.map((r) => r.id)
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.map((r) => r.id)
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.map((r) => r.id)
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: 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
  };
@@ -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 [users, setUsers] = react.useState([]);
809
- const [roles, setRoles] = react.useState([]);
810
- const [loading, setLoading] = react.useState(true);
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("/roles", "GET", void 0, 6, signal);
886
- setRoles(data.roles.map(convertRole));
887
- setRolesError(void 0);
888
- } catch (error) {
889
- if (error instanceof Error && error.name === "AbortError") return;
890
- console.error("Failed to load roles:", error);
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 load users:", error);
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 userRoles = currentUser.roles ?? [];
912
- const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
913
- if (!isUserAdmin) {
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 allUsers = data.users.map((u) => convertUser(u));
944
- setUsers(allUsers);
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 load users:", error);
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: data.users.map((u) => convertUser(u)),
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 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]);
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
- setUsers((prev) => [...prev, created]);
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, roles]);
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
- setUsers((prev) => prev.map((u) => u.uid === updatedUser.uid ? updatedUser : u));
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
- setUsers((prev) => prev.filter((u) => u.uid !== user.uid));
1028
- }, [apiRequest]);
1029
- const saveRole = react.useCallback(async (role) => {
1030
- const existingRole = roles.find((r) => r.id === role.id);
1031
- if (existingRole) {
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 users.find((u) => u.uid === uid) ?? null;
1056
- }, [users]);
1010
+ return userCache.get(uid) ?? null;
1011
+ }, [userCache]);
1057
1012
  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;
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 roles.filter((r) => userRoleIds.includes(r.id));
1062
- }, [users, roles]);
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
- const data = await apiRequest("/roles");
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, loadUsers]);
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;