@rebasepro/auth 0.3.0 → 0.5.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/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @rebasepro/auth
2
+
3
+ Custom JWT authentication adapter for the Rebase backend — React hooks and API utilities for email/password, OAuth, and session management.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rebasepro/auth
9
+ ```
10
+
11
+ **Peer dependencies:** `react >= 19`, `react-dom >= 19`
12
+
13
+ ## What This Package Does
14
+
15
+ `@rebasepro/auth` connects the Rebase CMS frontend to the backend's JWT authentication system. It provides a React hook (`useRebaseAuthController`) that manages login, registration, token refresh, session persistence, and user profile updates, plus a lower-level API module for direct HTTP calls to auth endpoints. For the generic `LoginView` and `RebaseAuth` UI components, see `@rebasepro/core`.
16
+
17
+ ## Key Exports
18
+
19
+ ### Hooks
20
+
21
+ | Export | Description |
22
+ |---|---|
23
+ | `useRebaseAuthController` | Main auth hook — returns a `RebaseAuthController` with login, register, logout, token refresh, session management, and profile methods. Accepts `RebaseAuthControllerProps`. |
24
+ | `useBackendUserManagement` | Hook for admin-level user CRUD (list, create, update, delete users). Returns a `UserManagement` interface. Accepts `BackendUserManagementConfig`. |
25
+
26
+ ### Types
27
+
28
+ | Export | Description |
29
+ |---|---|
30
+ | `RebaseAuthController` | Extends the base `AuthController` with email/password login, Google login, generic OAuth, registration, password reset/change, session management, and profile updates. |
31
+ | `RebaseAuthControllerProps` | Config for `useRebaseAuthController` — `client`, `apiUrl`, `googleClientId`, `onSignOut`, `defineRolesFor`. |
32
+ | `AuthTokens` | `{ accessToken, refreshToken, accessTokenExpiresAt }` |
33
+ | `UserInfo` | `{ uid, email, displayName, photoURL, emailVerified, roles, metadata }` |
34
+ | `AuthResponse` | `{ user: UserInfo, tokens: AuthTokens }` |
35
+ | `RefreshResponse` | `{ tokens: AuthTokens }` |
36
+ | `BackendUserManagementConfig` | Config for `useBackendUserManagement`. |
37
+ | `UserManagement` | Interface returned by `useBackendUserManagement`. |
38
+
39
+ ### API Utilities
40
+
41
+ | Export | Description |
42
+ |---|---|
43
+ | `setApiUrl(url)` | Set the base API URL for all auth requests |
44
+ | `getApiUrl()` | Get the current base API URL |
45
+ | `fetchAuthConfig()` | Fetch auth config from `/api/auth/config` (cached, deduplicated) |
46
+ | `AuthApiError` | Error class with `message` and `code` fields |
47
+ | `AuthConfigResponse` | Type — `{ needsSetup, registrationEnabled, emailServiceEnabled, passwordReset, emailVerification, enabledProviders }` |
48
+
49
+ ## Quick Start
50
+
51
+ ```tsx
52
+ import { useRebaseAuthController } from "@rebasepro/auth";
53
+ import { createRebaseClient } from "@rebasepro/client";
54
+
55
+ const client = createRebaseClient({ baseUrl: "http://localhost:3001" });
56
+
57
+ function App() {
58
+ const authController = useRebaseAuthController({
59
+ client,
60
+ apiUrl: "http://localhost:3001",
61
+ });
62
+
63
+ // authController.emailPasswordLogin(email, password)
64
+ // authController.googleLogin({ idToken })
65
+ // authController.register(email, password, displayName)
66
+ // authController.signOut()
67
+ // authController.user — current logged-in user or null
68
+ }
69
+ ```
70
+
71
+ ## Related Packages
72
+
73
+ - [`@rebasepro/core`](../core) — `LoginView`, `RebaseAuth` UI components, `AuthController` base type
74
+ - [`@rebasepro/client`](../client) — HTTP client with its own `auth` module for direct API calls
75
+ - [`@rebasepro/types`](../types) — Base `AuthController` and `User` types
76
+ - [`@rebasepro/ui`](../ui) — UI components used by auth views
@@ -1,4 +1,4 @@
1
- import { Role, User } from "@rebasepro/types";
1
+ import type { User } from "@rebasepro/types";
2
2
  /**
3
3
  * UserManagement interface - compatible with @rebasepro/user_management
4
4
  * Defined inline to avoid dependency on that package
@@ -19,12 +19,9 @@ export interface UserManagement<USER extends User = User> {
19
19
  temporaryPassword?: string;
20
20
  }>;
21
21
  deleteUser: (user: USER) => Promise<void>;
22
- roles: Role[];
23
- saveRole: (role: Role) => Promise<void>;
24
- deleteRole: (role: Role) => Promise<void>;
25
22
  isAdmin?: boolean;
26
23
  allowDefaultRolesCreation?: boolean;
27
- defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
24
+ defineRolesFor: (user: User) => Promise<string[] | undefined> | string[] | undefined;
28
25
  getUser: (uid: string) => User | null;
29
26
  /**
30
27
  * Search users with server-side pagination.
@@ -43,7 +40,6 @@ export interface UserManagement<USER extends User = User> {
43
40
  total: number;
44
41
  }>;
45
42
  usersError?: Error;
46
- rolesError?: Error;
47
43
  bootstrapAdmin?: () => Promise<void>;
48
44
  }
49
45
  export interface BackendUserManagementConfig {
package/dist/index.es.js CHANGED
@@ -425,7 +425,7 @@ function useRebaseAuthController(props = {}) {
425
425
  if (defineRolesFor) {
426
426
  const customRoles = await defineRolesFor(convertedUser);
427
427
  if (customRoles) {
428
- convertedUser = { ...convertedUser, roles: customRoles.map((r) => r.id) };
428
+ convertedUser = { ...convertedUser, roles: customRoles };
429
429
  }
430
430
  }
431
431
  saveAuthToStorage(tokens, userInfo);
@@ -556,7 +556,7 @@ function useRebaseAuthController(props = {}) {
556
556
  if (customRoles) {
557
557
  convertedUser = {
558
558
  ...convertedUser,
559
- roles: customRoles.map((r) => r.id)
559
+ roles: customRoles
560
560
  };
561
561
  }
562
562
  }
@@ -630,7 +630,7 @@ function useRebaseAuthController(props = {}) {
630
630
  if (customRoles) {
631
631
  userToSet = {
632
632
  ...userToSet,
633
- roles: customRoles.map((r) => r.id)
633
+ roles: customRoles
634
634
  };
635
635
  }
636
636
  }
@@ -662,7 +662,7 @@ function useRebaseAuthController(props = {}) {
662
662
  if (customRoles) {
663
663
  userToSet = {
664
664
  ...userToSet,
665
- roles: customRoles.map((r) => r.id)
665
+ roles: customRoles
666
666
  };
667
667
  }
668
668
  }
@@ -791,18 +791,10 @@ function convertUser(apiUser) {
791
791
  createdAt: apiUser.createdAt ? new Date(apiUser.createdAt) : null
792
792
  };
793
793
  }
794
- function convertRole(apiRole) {
795
- return {
796
- id: apiRole.id,
797
- name: apiRole.name,
798
- isAdmin: apiRole.isAdmin ?? false
799
- };
800
- }
801
794
  function useBackendUserManagement(config) {
802
795
  const { client, apiUrl, getAuthToken, currentUser } = config;
803
796
  const [userCache, setUserCache] = useState(/* @__PURE__ */ new Map());
804
797
  const [hasAdminUsers, setHasAdminUsers] = useState(false);
805
- const [roles, setRoles] = useState([]);
806
798
  const userRoles = currentUser?.roles ?? [];
807
799
  const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
808
800
  const [loading, setLoading] = useState(() => {
@@ -811,7 +803,6 @@ function useBackendUserManagement(config) {
811
803
  return true;
812
804
  });
813
805
  const [usersError, setUsersError] = useState();
814
- const [rolesError, setRolesError] = useState();
815
806
  const lastLoadedUidRef = useRef(null);
816
807
  const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
817
808
  const mergeIntoCache = useCallback((incoming) => {
@@ -892,17 +883,6 @@ function useBackendUserManagement(config) {
892
883
  throw lastError;
893
884
  }, [apiUrl, getAuthToken]);
894
885
  apiRequestRef.current = apiRequest;
895
- useCallback(async (signal) => {
896
- try {
897
- const data = await apiRequest("/roles", "GET", void 0, 6, signal);
898
- setRoles(data.roles.map(convertRole));
899
- setRolesError(void 0);
900
- } catch (error) {
901
- if (error instanceof Error && error.name === "AbortError") return;
902
- console.error("Failed to load roles:", error);
903
- setRolesError(error instanceof Error ? error : new Error(String(error)));
904
- }
905
- }, [apiRequest]);
906
886
  const checkAdminExists = useCallback(async (signal) => {
907
887
  try {
908
888
  const data = await apiRequest("/users?role=admin&limit=1", "GET", void 0, 6, signal);
@@ -937,21 +917,6 @@ function useBackendUserManagement(config) {
937
917
  const load = async () => {
938
918
  setLoading(true);
939
919
  const request = apiRequestRef.current;
940
- try {
941
- const data = await request("/roles", "GET", void 0, 6, abortController.signal);
942
- setRoles(data.roles.map(convertRole));
943
- setRolesError(void 0);
944
- } catch (error) {
945
- if (error instanceof Error && error.name === "AbortError") return;
946
- console.error("Failed to load roles:", error);
947
- setRolesError(error instanceof Error ? error : new Error(String(error)));
948
- const status = error.status;
949
- if (status === 403 || status === 401) {
950
- setUsersError(error instanceof Error ? error : new Error(String(error)));
951
- setLoading(false);
952
- return;
953
- }
954
- }
955
920
  if (!abortController.signal.aborted) {
956
921
  try {
957
922
  const data = await request("/users?role=admin&limit=1", "GET", void 0, 6, abortController.signal);
@@ -1038,29 +1003,6 @@ function useBackendUserManagement(config) {
1038
1003
  return next;
1039
1004
  });
1040
1005
  }, [apiRequest]);
1041
- const saveRole = useCallback(async (role) => {
1042
- const existingRole = roles.find((r) => r.id === role.id);
1043
- if (existingRole) {
1044
- const data = await apiRequest(`/roles/${role.id}`, "PUT", {
1045
- name: role.name,
1046
- isAdmin: role.isAdmin
1047
- });
1048
- const updated = convertRole(data.role);
1049
- setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
1050
- } else {
1051
- const data = await apiRequest("/roles", "POST", {
1052
- id: role.id,
1053
- name: role.name,
1054
- isAdmin: role.isAdmin ?? false
1055
- });
1056
- const created = convertRole(data.role);
1057
- setRoles((prev) => [...prev, created]);
1058
- }
1059
- }, [apiRequest, roles]);
1060
- const deleteRole = useCallback(async (role) => {
1061
- await apiRequest(`/roles/${role.id}`, "DELETE");
1062
- setRoles((prev) => prev.filter((r) => r.id !== role.id));
1063
- }, [apiRequest]);
1064
1006
  const getUser = useCallback((uid) => {
1065
1007
  return userCache.get(uid) ?? null;
1066
1008
  }, [userCache]);
@@ -1076,15 +1018,12 @@ function useBackendUserManagement(config) {
1076
1018
  }
1077
1019
  }
1078
1020
  const userRoleIds = existingUser.roles ?? [];
1079
- return roles.filter((r) => userRoleIds.includes(r.id));
1080
- }, [userCache, roles, apiRequest, mergeIntoCache]);
1021
+ return userRoleIds;
1022
+ }, [userCache, apiRequest, mergeIntoCache]);
1081
1023
  const isAdmin = currentUser?.roles?.includes("admin") ?? false;
1082
1024
  const bootstrapAdmin = useCallback(async () => {
1083
1025
  try {
1084
1026
  await apiRequest("/bootstrap", "POST");
1085
- const data = await apiRequest("/roles");
1086
- const loadedRoles = data.roles.map(convertRole);
1087
- setRoles(loadedRoles);
1088
1027
  await checkAdminExists();
1089
1028
  } catch (error) {
1090
1029
  console.error("Failed to bootstrap admin:", error);
@@ -1100,16 +1039,12 @@ function useBackendUserManagement(config) {
1100
1039
  createUser,
1101
1040
  resetPassword: resetPassword2,
1102
1041
  deleteUser,
1103
- roles,
1104
- saveRole,
1105
- deleteRole,
1106
1042
  isAdmin,
1107
1043
  allowDefaultRolesCreation: isAdmin,
1108
1044
  defineRolesFor,
1109
1045
  getUser,
1110
1046
  searchUsers,
1111
1047
  usersError,
1112
- rolesError,
1113
1048
  bootstrapAdmin
1114
1049
  };
1115
1050
  }