@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 CHANGED
@@ -132,7 +132,11 @@ export interface AuthConfigResponse {
132
132
  /** Whether new user registration is enabled */
133
133
  registrationEnabled: boolean;
134
134
  /** Whether email service is configured */
135
- emailServiceEnabled: boolean;
135
+ emailServiceEnabled?: boolean;
136
+ /** Whether password reset is supported */
137
+ passwordReset?: boolean;
138
+ /** Whether email verification is supported */
139
+ emailVerification?: boolean;
136
140
  /** Complete list of enabled OAuth provider IDs (e.g. ["google", "github", "discord"]) */
137
141
  enabledProviders: string[];
138
142
  }
@@ -5,6 +5,7 @@ import { Role, User } from "@rebasepro/types";
5
5
  */
6
6
  export interface UserManagement<USER extends User = User> {
7
7
  loading: boolean;
8
+ hasAdminUsers?: boolean;
8
9
  users: USER[];
9
10
  saveUser: (user: USER) => Promise<USER>;
10
11
  createUser?: (user: USER) => Promise<{
@@ -23,7 +24,6 @@ export interface UserManagement<USER extends User = User> {
23
24
  deleteRole: (role: Role) => Promise<void>;
24
25
  isAdmin?: boolean;
25
26
  allowDefaultRolesCreation?: boolean;
26
- includeCollectionConfigPermissions?: boolean;
27
27
  defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
28
28
  getUser: (uid: string) => User | null;
29
29
  /**
@@ -50,7 +50,10 @@ export interface BackendUserManagementConfig {
50
50
  /**
51
51
  * The Rebase Client instance
52
52
  */
53
- client?: any;
53
+ client?: {
54
+ baseUrl?: string;
55
+ resolveToken?: () => Promise<string | null>;
56
+ };
54
57
  /**
55
58
  * Base API URL for the backend (optional, extracted from client if not provided)
56
59
  */
package/dist/index.d.ts CHANGED
@@ -10,5 +10,5 @@ export type { RebaseAuthController, RebaseAuthControllerProps, AuthTokens, UserI
10
10
  export { useRebaseAuthController } from "./hooks/useRebaseAuthController";
11
11
  export { useBackendUserManagement } from "./hooks/useBackendUserManagement";
12
12
  export type { BackendUserManagementConfig, UserManagement } from "./hooks/useBackendUserManagement";
13
- export { setApiUrl, getApiUrl, fetchAuthConfig, clearAuthConfigCache, AuthApiError } from "./api";
13
+ export { setApiUrl, getApiUrl, fetchAuthConfig, AuthApiError } from "./api";
14
14
  export type { AuthConfigResponse } from "./api";
package/dist/index.es.js CHANGED
@@ -214,11 +214,7 @@ async function fetchAuthConfig() {
214
214
  authConfigInflight = null;
215
215
  }
216
216
  }
217
- function clearAuthConfigCache() {
218
- authConfigCached = null;
219
- authConfigInflight = null;
220
- }
221
- const STORAGE_KEY = "rebase_auth";
217
+ const STORAGE_KEY = "rebase_react_auth";
222
218
  const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1e3;
223
219
  function convertToUser(userInfo) {
224
220
  return {
@@ -276,12 +272,11 @@ function useRebaseAuthController(props = {}) {
276
272
  const refreshPromiseRef = useRef(null);
277
273
  const isMountedRef = useRef(true);
278
274
  useEffect(() => {
279
- if (client) {
280
- setApiUrl(client.baseUrl);
281
- } else if (apiUrl) {
282
- setApiUrl(apiUrl);
275
+ const url = client?.baseUrl || apiUrl;
276
+ if (url) {
277
+ setApiUrl(url);
283
278
  }
284
- }, [client, apiUrl]);
279
+ }, [client, client?.baseUrl, apiUrl]);
285
280
  const clearError = useCallback(() => {
286
281
  setAuthProviderError(null);
287
282
  }, []);
@@ -397,7 +392,7 @@ function useRebaseAuthController(props = {}) {
397
392
  }, [initialLoading, refreshAccessToken$1, clearSessionAndSignOut]);
398
393
  useEffect(() => {
399
394
  if (client) {
400
- client.setAuthTokenGetter(async () => {
395
+ client.setAuthTokenGetter?.(async () => {
401
396
  try {
402
397
  return await getAuthToken();
403
398
  } catch {
@@ -673,6 +668,10 @@ function useRebaseAuthController(props = {}) {
673
668
  }
674
669
  } catch (meError) {
675
670
  if (!isMountedRef.current) return;
671
+ if (meError instanceof AuthApiError && (meError.code === "NOT_FOUND" || meError.code === "UNAUTHORIZED")) {
672
+ clearSessionAndSignOut();
673
+ return;
674
+ }
676
675
  userToSet = convertToUser(stored.user);
677
676
  }
678
677
  if (!isMountedRef.current) return;
@@ -772,10 +771,10 @@ function useRebaseAuthController(props = {}) {
772
771
  emailPasswordLogin: true,
773
772
  googleLogin: !!props.googleClientId,
774
773
  registration: authConfig?.registrationEnabled ?? false,
775
- passwordReset: true,
774
+ passwordReset: authConfig?.passwordReset ?? false,
776
775
  sessionManagement: true,
777
776
  profileUpdate: true,
778
- emailVerification: false,
777
+ emailVerification: authConfig?.emailVerification ?? false,
779
778
  enabledProviders: authConfig?.enabledProviders ?? []
780
779
  }
781
780
  };
@@ -796,18 +795,34 @@ function convertRole(apiRole) {
796
795
  return {
797
796
  id: apiRole.id,
798
797
  name: apiRole.name,
799
- isAdmin: apiRole.isAdmin ?? false,
800
- config: apiRole.config ?? void 0
798
+ isAdmin: apiRole.isAdmin ?? false
801
799
  };
802
800
  }
803
801
  function useBackendUserManagement(config) {
804
802
  const { client, apiUrl, getAuthToken, currentUser } = config;
805
- const [users, setUsers] = useState([]);
803
+ const [userCache, setUserCache] = useState(/* @__PURE__ */ new Map());
804
+ const [hasAdminUsers, setHasAdminUsers] = useState(false);
806
805
  const [roles, setRoles] = useState([]);
807
- const [loading, setLoading] = useState(true);
806
+ const userRoles = currentUser?.roles ?? [];
807
+ const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
808
+ const [loading, setLoading] = useState(() => {
809
+ if (!currentUser) return false;
810
+ if (!isUserAdmin) return false;
811
+ return true;
812
+ });
808
813
  const [usersError, setUsersError] = useState();
809
814
  const [rolesError, setRolesError] = useState();
810
815
  const lastLoadedUidRef = useRef(null);
816
+ const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
817
+ const mergeIntoCache = useCallback((incoming) => {
818
+ setUserCache((prev) => {
819
+ const next = new Map(prev);
820
+ for (const u of incoming) {
821
+ next.set(u.uid, u);
822
+ }
823
+ return next;
824
+ });
825
+ }, []);
811
826
  const apiRequestRef = useRef(null);
812
827
  const apiRequest = useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
813
828
  let lastError = null;
@@ -818,7 +833,7 @@ function useBackendUserManagement(config) {
818
833
  throw error;
819
834
  }
820
835
  try {
821
- const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
836
+ const token = getAuthToken ? await getAuthToken() : client?.resolveToken ? await client.resolveToken() : null;
822
837
  const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
823
838
  const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
824
839
  method,
@@ -888,26 +903,29 @@ function useBackendUserManagement(config) {
888
903
  setRolesError(error instanceof Error ? error : new Error(String(error)));
889
904
  }
890
905
  }, [apiRequest]);
891
- const loadUsers = useCallback(async (signal) => {
906
+ const checkAdminExists = useCallback(async (signal) => {
892
907
  try {
893
- const data = await apiRequest("/users", "GET", void 0, 6, signal);
894
- const allUsers = data.users.map((u) => convertUser(u));
895
- setUsers(allUsers);
908
+ const data = await apiRequest("/users?role=admin&limit=1", "GET", void 0, 6, signal);
909
+ const adminUsers = data.users.map((u) => convertUser(u));
910
+ setHasAdminUsers(adminUsers.length > 0);
911
+ if (adminUsers.length > 0) {
912
+ mergeIntoCache(adminUsers);
913
+ }
896
914
  setUsersError(void 0);
897
915
  } catch (error) {
898
916
  if (error instanceof Error && error.name === "AbortError") return;
899
- console.error("Failed to load users:", error);
917
+ console.error("Failed to check admin users:", error);
900
918
  setUsersError(error instanceof Error ? error : new Error(String(error)));
901
919
  }
902
- }, [apiRequest]);
920
+ }, [apiRequest, mergeIntoCache]);
903
921
  useEffect(() => {
904
922
  if (!currentUser) {
905
923
  setLoading(false);
906
924
  return;
907
925
  }
908
- const userRoles = currentUser.roles ?? [];
909
- const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
910
- if (!isUserAdmin) {
926
+ const userRoles2 = currentUser.roles ?? [];
927
+ const isUserAdmin2 = userRoles2.some((r) => r === "admin" || r === "schema-admin");
928
+ if (!isUserAdmin2) {
911
929
  setLoading(false);
912
930
  return;
913
931
  }
@@ -936,13 +954,16 @@ function useBackendUserManagement(config) {
936
954
  }
937
955
  if (!abortController.signal.aborted) {
938
956
  try {
939
- const data = await request("/users", "GET", void 0, 6, abortController.signal);
940
- const allUsers = data.users.map((u) => convertUser(u));
941
- setUsers(allUsers);
957
+ const data = await request("/users?role=admin&limit=1", "GET", void 0, 6, abortController.signal);
958
+ const adminUsers = data.users.map((u) => convertUser(u));
959
+ setHasAdminUsers(adminUsers.length > 0);
960
+ if (adminUsers.length > 0) {
961
+ mergeIntoCache(adminUsers);
962
+ }
942
963
  setUsersError(void 0);
943
964
  } catch (error) {
944
965
  if (error instanceof Error && error.name === "AbortError") return;
945
- console.error("Failed to load users:", error);
966
+ console.error("Failed to check admin users:", error);
946
967
  setUsersError(error instanceof Error ? error : new Error(String(error)));
947
968
  }
948
969
  }
@@ -966,34 +987,24 @@ function useBackendUserManagement(config) {
966
987
  if (options.roleId) params.set("role", options.roleId);
967
988
  const qs = params.toString();
968
989
  const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
990
+ const converted = data.users.map((u) => convertUser(u));
991
+ mergeIntoCache(converted);
969
992
  return {
970
- users: data.users.map((u) => convertUser(u)),
993
+ users: converted,
971
994
  total: data.total
972
995
  };
973
- }, [apiRequest]);
996
+ }, [apiRequest, mergeIntoCache]);
974
997
  const saveUser = useCallback(async (user) => {
975
998
  const roleIds = user.roles ?? [];
976
- const existingUser = users.find((u) => u.uid === user.uid);
977
- if (existingUser) {
978
- const data = await apiRequest(`/users/${user.uid}`, "PUT", {
979
- email: user.email,
980
- displayName: user.displayName,
981
- roles: roleIds
982
- });
983
- const updated = convertUser(data.user);
984
- setUsers((prev) => prev.map((u) => u.uid === updated.uid ? updated : u));
985
- return updated;
986
- } else {
987
- const data = await apiRequest("/users", "POST", {
988
- email: user.email,
989
- displayName: user.displayName,
990
- roles: roleIds
991
- });
992
- const created = convertUser(data.user);
993
- setUsers((prev) => [...prev, created]);
994
- return created;
995
- }
996
- }, [apiRequest, users, roles]);
999
+ const data = await apiRequest(`/users/${user.uid}`, "PUT", {
1000
+ email: user.email,
1001
+ displayName: user.displayName,
1002
+ roles: roleIds
1003
+ });
1004
+ const updated = convertUser(data.user);
1005
+ mergeIntoCache([updated]);
1006
+ return updated;
1007
+ }, [apiRequest, mergeIntoCache]);
997
1008
  const createUser = useCallback(async (user) => {
998
1009
  const roleIds = user.roles ?? [];
999
1010
  const data = await apiRequest("/users", "POST", {
@@ -1002,34 +1013,37 @@ function useBackendUserManagement(config) {
1002
1013
  roles: roleIds
1003
1014
  });
1004
1015
  const created = convertUser(data.user);
1005
- setUsers((prev) => [...prev, created]);
1016
+ mergeIntoCache([created]);
1006
1017
  return {
1007
1018
  user: created,
1008
1019
  invitationSent: data.invitationSent ?? false,
1009
1020
  temporaryPassword: data.temporaryPassword
1010
1021
  };
1011
- }, [apiRequest, roles]);
1022
+ }, [apiRequest, mergeIntoCache]);
1012
1023
  const resetPassword2 = useCallback(async (user) => {
1013
1024
  const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
1014
1025
  const updatedUser = convertUser(data.user);
1015
- setUsers((prev) => prev.map((u) => u.uid === updatedUser.uid ? updatedUser : u));
1026
+ mergeIntoCache([updatedUser]);
1016
1027
  return {
1017
1028
  user: updatedUser,
1018
1029
  invitationSent: data.invitationSent ?? false,
1019
1030
  temporaryPassword: data.temporaryPassword
1020
1031
  };
1021
- }, [apiRequest]);
1032
+ }, [apiRequest, mergeIntoCache]);
1022
1033
  const deleteUser = useCallback(async (user) => {
1023
1034
  await apiRequest(`/users/${user.uid}`, "DELETE");
1024
- setUsers((prev) => prev.filter((u) => u.uid !== user.uid));
1035
+ setUserCache((prev) => {
1036
+ const next = new Map(prev);
1037
+ next.delete(user.uid);
1038
+ return next;
1039
+ });
1025
1040
  }, [apiRequest]);
1026
1041
  const saveRole = useCallback(async (role) => {
1027
1042
  const existingRole = roles.find((r) => r.id === role.id);
1028
1043
  if (existingRole) {
1029
1044
  const data = await apiRequest(`/roles/${role.id}`, "PUT", {
1030
1045
  name: role.name,
1031
- isAdmin: role.isAdmin,
1032
- config: role.config
1046
+ isAdmin: role.isAdmin
1033
1047
  });
1034
1048
  const updated = convertRole(data.role);
1035
1049
  setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
@@ -1037,8 +1051,7 @@ function useBackendUserManagement(config) {
1037
1051
  const data = await apiRequest("/roles", "POST", {
1038
1052
  id: role.id,
1039
1053
  name: role.name,
1040
- isAdmin: role.isAdmin ?? false,
1041
- config: role.config
1054
+ isAdmin: role.isAdmin ?? false
1042
1055
  });
1043
1056
  const created = convertRole(data.role);
1044
1057
  setRoles((prev) => [...prev, created]);
@@ -1049,14 +1062,22 @@ function useBackendUserManagement(config) {
1049
1062
  setRoles((prev) => prev.filter((r) => r.id !== role.id));
1050
1063
  }, [apiRequest]);
1051
1064
  const getUser = useCallback((uid) => {
1052
- return users.find((u) => u.uid === uid) ?? null;
1053
- }, [users]);
1065
+ return userCache.get(uid) ?? null;
1066
+ }, [userCache]);
1054
1067
  const defineRolesFor = useCallback(async (user) => {
1055
- const existingUser = users.find((u) => u.uid === user.uid || u.email === user.email);
1056
- if (!existingUser) return void 0;
1068
+ let existingUser = userCache.get(user.uid) ?? Array.from(userCache.values()).find((u) => u.email === user.email);
1069
+ if (!existingUser) {
1070
+ try {
1071
+ const data = await apiRequest(`/users/${user.uid}`, "GET");
1072
+ existingUser = convertUser(data.user);
1073
+ mergeIntoCache([existingUser]);
1074
+ } catch {
1075
+ return void 0;
1076
+ }
1077
+ }
1057
1078
  const userRoleIds = existingUser.roles ?? [];
1058
1079
  return roles.filter((r) => userRoleIds.includes(r.id));
1059
- }, [users, roles]);
1080
+ }, [userCache, roles, apiRequest, mergeIntoCache]);
1060
1081
  const isAdmin = currentUser?.roles?.includes("admin") ?? false;
1061
1082
  const bootstrapAdmin = useCallback(async () => {
1062
1083
  try {
@@ -1064,15 +1085,17 @@ function useBackendUserManagement(config) {
1064
1085
  const data = await apiRequest("/roles");
1065
1086
  const loadedRoles = data.roles.map(convertRole);
1066
1087
  setRoles(loadedRoles);
1067
- await loadUsers();
1088
+ await checkAdminExists();
1068
1089
  } catch (error) {
1069
1090
  console.error("Failed to bootstrap admin:", error);
1070
1091
  throw error;
1071
1092
  }
1072
- }, [apiRequest, loadUsers]);
1093
+ }, [apiRequest, checkAdminExists]);
1094
+ const users = Array.from(userCache.values());
1073
1095
  return {
1074
- loading,
1096
+ loading: effectiveLoading,
1075
1097
  users,
1098
+ hasAdminUsers,
1076
1099
  saveUser,
1077
1100
  createUser,
1078
1101
  resetPassword: resetPassword2,
@@ -1082,7 +1105,6 @@ function useBackendUserManagement(config) {
1082
1105
  deleteRole,
1083
1106
  isAdmin,
1084
1107
  allowDefaultRolesCreation: isAdmin,
1085
- includeCollectionConfigPermissions: true,
1086
1108
  defineRolesFor,
1087
1109
  getUser,
1088
1110
  searchUsers,
@@ -1093,7 +1115,6 @@ function useBackendUserManagement(config) {
1093
1115
  }
1094
1116
  export {
1095
1117
  AuthApiError,
1096
- clearAuthConfigCache,
1097
1118
  fetchAuthConfig,
1098
1119
  getApiUrl,
1099
1120
  setApiUrl,