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