@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.
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import { Role, User } from "@rebasepro/types";
2
+ import type { User } from "@rebasepro/types";
3
3
 
4
4
  /**
5
5
  * UserManagement interface - compatible with @rebasepro/user_management
@@ -7,6 +7,7 @@ import { Role, User } from "@rebasepro/types";
7
7
  */
8
8
  export interface UserManagement<USER extends User = User> {
9
9
  loading: boolean;
10
+ hasAdminUsers?: boolean;
10
11
 
11
12
  users: USER[];
12
13
  saveUser: (user: USER) => Promise<USER>;
@@ -22,14 +23,9 @@ export interface UserManagement<USER extends User = User> {
22
23
  }>;
23
24
  deleteUser: (user: USER) => Promise<void>;
24
25
 
25
- roles: Role[];
26
- saveRole: (role: Role) => Promise<void>;
27
- deleteRole: (role: Role) => Promise<void>;
28
-
29
26
  isAdmin?: boolean;
30
27
  allowDefaultRolesCreation?: boolean;
31
- includeCollectionConfigPermissions?: boolean;
32
- defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
28
+ defineRolesFor: (user: User) => Promise<string[] | undefined> | string[] | undefined;
33
29
  getUser: (uid: string) => User | null;
34
30
 
35
31
  /**
@@ -47,7 +43,6 @@ export interface UserManagement<USER extends User = User> {
47
43
  }) => Promise<{ users: USER[]; total: number }>;
48
44
 
49
45
  usersError?: Error;
50
- rolesError?: Error;
51
46
  bootstrapAdmin?: () => Promise<void>;
52
47
  }
53
48
 
@@ -55,7 +50,7 @@ export interface BackendUserManagementConfig {
55
50
  /**
56
51
  * The Rebase Client instance
57
52
  */
58
- client?: any;
53
+ client?: { baseUrl?: string; resolveToken?: () => Promise<string | null> };
59
54
 
60
55
  /**
61
56
  * Base API URL for the backend (optional, extracted from client if not provided)
@@ -83,17 +78,13 @@ interface ApiUser {
83
78
  updatedAt?: string;
84
79
  }
85
80
 
86
- interface ApiRole {
87
- id: string;
88
- name: string;
89
- isAdmin?: boolean;
90
- config?: Record<string, any>;
91
- }
81
+ /** Response shapes from the admin API */
82
+ interface ApiUsersResponse { users: ApiUser[]; total: number }
83
+ interface ApiUserResponse { user: ApiUser; invitationSent?: boolean; temporaryPassword?: string }
92
84
 
93
85
  /**
94
86
  * Convert API user to Rebase User
95
87
  * @param apiUser - The API user object
96
- * @param availableRoles - Optional array of available roles to look up names
97
88
  */
98
89
  function convertUser(apiUser: ApiUser): User {
99
90
  return {
@@ -108,18 +99,6 @@ function convertUser(apiUser: ApiUser): User {
108
99
  } as User;
109
100
  }
110
101
 
111
- /**
112
- * Convert API role to Rebase Role
113
- */
114
- function convertRole(apiRole: ApiRole): Role {
115
- return {
116
- id: apiRole.id,
117
- name: apiRole.name,
118
- isAdmin: apiRole.isAdmin ?? false,
119
- config: apiRole.config ?? undefined
120
- };
121
- }
122
-
123
102
  /**
124
103
  * Hook to manage users and roles via backend API
125
104
  * Compatible with Rebase UserManagement interface
@@ -127,18 +106,37 @@ function convertRole(apiRole: ApiRole): Role {
127
106
  export function useBackendUserManagement(config: BackendUserManagementConfig): UserManagement {
128
107
  const { client, apiUrl, getAuthToken, currentUser } = config;
129
108
 
130
- // We no longer load ALL users into memory.
131
- // `users` now only holds admin/role-bearing users for getUser/defineRolesFor lookups.
132
- const [users, setUsers] = useState<User[]>([]);
133
- const [roles, setRoles] = useState<Role[]>([]);
134
- const [loading, setLoading] = useState(true);
109
+ // Lazy user cache populated on demand from search results, saves, and
110
+ // individual API lookups. We never load ALL users into memory.
111
+ const [userCache, setUserCache] = useState<Map<string, User>>(new Map());
112
+ const [hasAdminUsers, setHasAdminUsers] = useState(false);
113
+ const userRoles = currentUser?.roles ?? [];
114
+ const isUserAdmin = userRoles.some(r => r === "admin" || r === "schema-admin");
115
+
116
+ const [loading, setLoading] = useState(() => {
117
+ if (!currentUser) return false;
118
+ if (!isUserAdmin) return false;
119
+ return true;
120
+ });
135
121
  const [usersError, setUsersError] = useState<Error | undefined>();
136
- const [rolesError, setRolesError] = useState<Error | undefined>();
137
122
 
138
123
  // Tracks the UID for which roles+users were last successfully loaded.
139
124
  // Prevents redundant refetches on React StrictMode double-mounts.
140
125
  const lastLoadedUidRef = useRef<string | null>(null);
141
126
 
127
+ const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
128
+
129
+ /** Merge one or more users into the cache without replacing the whole Map. */
130
+ const mergeIntoCache = useCallback((incoming: User[]) => {
131
+ setUserCache(prev => {
132
+ const next = new Map(prev);
133
+ for (const u of incoming) {
134
+ next.set(u.uid, u);
135
+ }
136
+ return next;
137
+ });
138
+ }, []);
139
+
142
140
  // Ref to hold the latest apiRequest so the initial-load effect doesn't
143
141
  // re-trigger every time the callback identity changes.
144
142
  const apiRequestRef = useRef<typeof apiRequest | null>(null);
@@ -146,13 +144,13 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
146
144
  /**
147
145
  * Make authenticated API request
148
146
  */
149
- const apiRequest = useCallback(async (
147
+ const apiRequest = useCallback(async <T = Record<string, unknown>>(
150
148
  endpoint: string,
151
149
  method = "GET",
152
150
  body?: Record<string, unknown>,
153
151
  retryCount = 6,
154
152
  signal?: AbortSignal
155
- ): Promise<any> => {
153
+ ): Promise<T> => {
156
154
  let lastError: Error | null = null;
157
155
  for (let attempt = 0; attempt < retryCount; attempt++) {
158
156
  if (signal?.aborted) {
@@ -163,7 +161,7 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
163
161
 
164
162
  try {
165
163
  // Determine token provider
166
- const token = getAuthToken ? await getAuthToken() : (client ? await client.resolveToken() : null);
164
+ const token = getAuthToken ? await getAuthToken() : (client?.resolveToken ? await client.resolveToken() : null);
167
165
  const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
168
166
 
169
167
  // Use /api/admin prefix for admin endpoints
@@ -237,37 +235,28 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
237
235
  // Keep the ref in sync after every render.
238
236
  apiRequestRef.current = apiRequest;
239
237
 
240
- /**
241
- * Load roles from API
242
- */
243
- const loadRoles = useCallback(async (signal?: AbortSignal) => {
244
- try {
245
- const data = await apiRequest("/roles", "GET", undefined, 6, signal);
246
- setRoles(data.roles.map(convertRole));
247
- setRolesError(undefined);
248
- } catch (error: unknown) {
249
- if (error instanceof Error && error.name === "AbortError") return;
250
- console.error("Failed to load roles:", error);
251
- setRolesError(error instanceof Error ? error : new Error(String(error)));
252
- }
253
- }, [apiRequest]);
238
+
254
239
 
255
240
  /**
256
- * Load users for getUser/defineRolesFor lookups and for UserSelect dropdowns.
241
+ * Lightweight admin-existence check: fetch a single admin user.
242
+ * Used by the BootstrapAdminBanner to decide whether to show.
257
243
  */
258
- const loadUsers = useCallback(async (signal?: AbortSignal) => {
244
+ const checkAdminExists = useCallback(async (signal?: AbortSignal) => {
259
245
  try {
260
- // Load all users to satisfy Rebase CMS UserSelect field bindings
261
- const data = await apiRequest("/users", "GET", undefined, 6, signal);
262
- const allUsers: User[] = data.users.map((u: ApiUser) => convertUser(u));
263
- setUsers(allUsers);
246
+ const data = await apiRequest<ApiUsersResponse>("/users?role=admin&limit=1", "GET", undefined, 6, signal);
247
+ const adminUsers: User[] = data.users.map((u: ApiUser) => convertUser(u));
248
+ setHasAdminUsers(adminUsers.length > 0);
249
+ // Also cache these admin users for getUser lookups
250
+ if (adminUsers.length > 0) {
251
+ mergeIntoCache(adminUsers);
252
+ }
264
253
  setUsersError(undefined);
265
254
  } catch (error: unknown) {
266
255
  if (error instanceof Error && error.name === "AbortError") return;
267
- console.error("Failed to load users:", error);
256
+ console.error("Failed to check admin users:", error);
268
257
  setUsersError(error instanceof Error ? error : new Error(String(error)));
269
258
  }
270
- }, [apiRequest]);
259
+ }, [apiRequest, mergeIntoCache]);
271
260
 
272
261
  /**
273
262
  * Initial data load - only when user is logged in
@@ -306,37 +295,19 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
306
295
  setLoading(true);
307
296
  const request = apiRequestRef.current!;
308
297
 
309
- // Load roles first
310
- try {
311
- const data = await request("/roles", "GET", undefined, 6, abortController.signal);
312
- setRoles(data.roles.map(convertRole));
313
- setRolesError(undefined);
314
- } catch (error: unknown) {
315
- if (error instanceof Error && error.name === "AbortError") return;
316
- console.error("Failed to load roles:", error);
317
- setRolesError(error instanceof Error ? error : new Error(String(error)));
318
-
319
- // If the error is a permission issue (e.g. 403), skip loading
320
- // users — they will fail with the same error and we'd show a
321
- // duplicate snackbar / error message.
322
- const status = (error as { status?: number }).status;
323
- if (status === 403 || status === 401) {
324
- setUsersError(error instanceof Error ? error : new Error(String(error)));
325
- setLoading(false);
326
- return;
327
- }
328
- }
329
-
330
- // Then load all users if not aborted
298
+ // Lightweight admin-existence check (NOT loading all users)
331
299
  if (!abortController.signal.aborted) {
332
300
  try {
333
- const data = await request("/users", "GET", undefined, 6, abortController.signal);
334
- const allUsers: User[] = data.users.map((u: ApiUser) => convertUser(u));
335
- setUsers(allUsers);
301
+ const data = await request<ApiUsersResponse>("/users?role=admin&limit=1", "GET", undefined, 6, abortController.signal);
302
+ const adminUsers: User[] = data.users.map((u: ApiUser) => convertUser(u));
303
+ setHasAdminUsers(adminUsers.length > 0);
304
+ if (adminUsers.length > 0) {
305
+ mergeIntoCache(adminUsers);
306
+ }
336
307
  setUsersError(undefined);
337
308
  } catch (error: unknown) {
338
309
  if (error instanceof Error && error.name === "AbortError") return;
339
- console.error("Failed to load users:", error);
310
+ console.error("Failed to check admin users:", error);
340
311
  setUsersError(error instanceof Error ? error : new Error(String(error)));
341
312
  }
342
313
  }
@@ -357,6 +328,7 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
357
328
  /**
358
329
  * Search users with server-side pagination.
359
330
  * This is the primary method used by the UsersView table.
331
+ * Results are also merged into the cache for getUser lookups.
360
332
  */
361
333
  const searchUsers = useCallback(async (options: {
362
334
  search?: string;
@@ -375,44 +347,31 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
375
347
  if (options.roleId) params.set("role", options.roleId);
376
348
  const qs = params.toString();
377
349
 
378
- const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
350
+ const data = await apiRequest<ApiUsersResponse>("/users" + (qs ? "?" + qs : ""), "GET");
351
+ const converted = data.users.map((u: ApiUser) => convertUser(u));
352
+ // Feed search results into cache for getUser/defineRolesFor
353
+ mergeIntoCache(converted);
379
354
  return {
380
- users: data.users.map((u: ApiUser) => convertUser(u)),
355
+ users: converted,
381
356
  total: data.total
382
357
  };
383
- }, [apiRequest]);
358
+ }, [apiRequest, mergeIntoCache]);
384
359
 
385
360
  /**
386
- * Save user (create or update)
361
+ * Save user (update existing user)
387
362
  */
388
363
  const saveUser = useCallback(async (user: User): Promise<User> => {
389
364
  const roleIds = user.roles ?? [];
390
365
 
391
- // Check if user exists
392
- const existingUser = users.find(u => u.uid === user.uid);
393
-
394
- if (existingUser) {
395
- // Update
396
- const data = await apiRequest(`/users/${user.uid}`, "PUT", {
397
- email: user.email,
398
- displayName: user.displayName,
399
- roles: roleIds
400
- });
401
- const updated = convertUser(data.user);
402
- setUsers(prev => prev.map(u => u.uid === updated.uid ? updated : u));
403
- return updated;
404
- } else {
405
- // Create
406
- const data = await apiRequest("/users", "POST", {
407
- email: user.email,
408
- displayName: user.displayName,
409
- roles: roleIds
410
- });
411
- const created = convertUser(data.user);
412
- setUsers(prev => [...prev, created]);
413
- return created;
414
- }
415
- }, [apiRequest, users, roles]);
366
+ const data = await apiRequest<ApiUserResponse>(`/users/${user.uid}`, "PUT", {
367
+ email: user.email,
368
+ displayName: user.displayName,
369
+ roles: roleIds
370
+ });
371
+ const updated = convertUser(data.user);
372
+ mergeIntoCache([updated]);
373
+ return updated;
374
+ }, [apiRequest, mergeIntoCache]);
416
375
 
417
376
  /**
418
377
  * Create a new user with invitation/password generation support.
@@ -425,20 +384,19 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
425
384
  }> => {
426
385
  const roleIds = user.roles ?? [];
427
386
 
428
- const data = await apiRequest("/users", "POST", {
387
+ const data = await apiRequest<ApiUserResponse>("/users", "POST", {
429
388
  email: user.email,
430
389
  displayName: user.displayName,
431
390
  roles: roleIds
432
391
  });
433
392
  const created = convertUser(data.user);
434
- // Add to users cache
435
- setUsers(prev => [...prev, created]);
393
+ mergeIntoCache([created]);
436
394
  return {
437
395
  user: created,
438
396
  invitationSent: data.invitationSent ?? false,
439
397
  temporaryPassword: data.temporaryPassword
440
398
  };
441
- }, [apiRequest, roles]);
399
+ }, [apiRequest, mergeIntoCache]);
442
400
 
443
401
  /**
444
402
  * Reset the password for an existing user
@@ -448,80 +406,60 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
448
406
  invitationSent: boolean;
449
407
  temporaryPassword?: string;
450
408
  }> => {
451
- const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
409
+ const data = await apiRequest<ApiUserResponse>(`/users/${user.uid}/reset-password`, "POST");
452
410
  const updatedUser = convertUser(data.user);
453
- setUsers(prev => prev.map(u => u.uid === updatedUser.uid ? updatedUser : u));
411
+ mergeIntoCache([updatedUser]);
454
412
  return {
455
413
  user: updatedUser,
456
414
  invitationSent: data.invitationSent ?? false,
457
415
  temporaryPassword: data.temporaryPassword
458
416
  };
459
- }, [apiRequest]);
417
+ }, [apiRequest, mergeIntoCache]);
460
418
 
461
419
  /**
462
420
  * Delete user
463
421
  */
464
422
  const deleteUser = useCallback(async (user: User): Promise<void> => {
465
423
  await apiRequest(`/users/${user.uid}`, "DELETE");
466
- setUsers(prev => prev.filter(u => u.uid !== user.uid));
424
+ setUserCache(prev => {
425
+ const next = new Map(prev);
426
+ next.delete(user.uid);
427
+ return next;
428
+ });
467
429
  }, [apiRequest]);
468
430
 
469
- /**
470
- * Save role (create or update)
471
- */
472
- const saveRole = useCallback(async (role: Role): Promise<void> => {
473
- // Check if role exists
474
- const existingRole = roles.find(r => r.id === role.id);
475
-
476
- if (existingRole) {
477
- // Update
478
- const data = await apiRequest(`/roles/${role.id}`, "PUT", {
479
- name: role.name,
480
- isAdmin: role.isAdmin,
481
- config: role.config
482
- });
483
- const updated = convertRole(data.role);
484
- setRoles(prev => prev.map(r => r.id === updated.id ? updated : r));
485
- } else {
486
- // Create
487
- const data = await apiRequest("/roles", "POST", {
488
- id: role.id,
489
- name: role.name,
490
- isAdmin: role.isAdmin ?? false,
491
- config: role.config
492
- });
493
- const created = convertRole(data.role);
494
- setRoles(prev => [...prev, created]);
495
- }
496
- }, [apiRequest, roles]);
497
431
 
498
- /**
499
- * Delete role
500
- */
501
- const deleteRole = useCallback(async (role: Role): Promise<void> => {
502
- await apiRequest(`/roles/${role.id}`, "DELETE");
503
- setRoles(prev => prev.filter(r => r.id !== role.id));
504
- }, [apiRequest]);
505
432
 
506
433
  /**
507
434
  * Get user by uid
508
435
  */
509
436
  const getUser = useCallback((uid: string): User | null => {
510
- return users.find(u => u.uid === uid) ?? null;
511
- }, [users]);
437
+ return userCache.get(uid) ?? null;
438
+ }, [userCache]);
512
439
 
513
440
  /**
514
441
  * Define roles for a given user (for authController)
515
442
  */
516
- const defineRolesFor = useCallback(async (user: User): Promise<Role[] | undefined> => {
517
- // Find the user in our list
518
- const existingUser = users.find(u => u.uid === user.uid || u.email === user.email);
519
- if (!existingUser) return undefined;
443
+ const defineRolesFor = useCallback(async (user: User): Promise<string[] | undefined> => {
444
+ // Check cache first
445
+ let existingUser = userCache.get(user.uid)
446
+ ?? Array.from(userCache.values()).find(u => u.email === user.email);
447
+
448
+ // If not cached, fetch from API
449
+ if (!existingUser) {
450
+ try {
451
+ const data = await apiRequest<ApiUserResponse>(`/users/${user.uid}`, "GET");
452
+ existingUser = convertUser(data.user);
453
+ mergeIntoCache([existingUser]);
454
+ } catch {
455
+ return undefined;
456
+ }
457
+ }
520
458
 
521
- // Return roles from our cached role data (string IDs full Role objects)
459
+ // Return role IDs as simple strings
522
460
  const userRoleIds = existingUser.roles ?? [];
523
- return roles.filter(r => userRoleIds.includes(r.id));
524
- }, [users, roles]);
461
+ return userRoleIds;
462
+ }, [userCache, apiRequest, mergeIntoCache]);
525
463
 
526
464
  /**
527
465
  * Check if current user is admin
@@ -535,35 +473,32 @@ export function useBackendUserManagement(config: BackendUserManagementConfig): U
535
473
  const bootstrapAdmin = useCallback(async (): Promise<void> => {
536
474
  try {
537
475
  await apiRequest("/bootstrap", "POST");
538
- // Reload users and roles after successful bootstrap
539
- const data = await apiRequest("/roles");
540
- const loadedRoles = data.roles.map(convertRole);
541
- setRoles(loadedRoles);
542
- await loadUsers();
476
+ // Re-check admin existence after successful bootstrap
477
+ await checkAdminExists();
543
478
  } catch (error) {
544
479
  console.error("Failed to bootstrap admin:", error);
545
480
  throw error;
546
481
  }
547
- }, [apiRequest, loadUsers]);
482
+ }, [apiRequest, checkAdminExists]);
483
+
484
+ // Expose cached users as an array for backward compat (BootstrapAdminBanner,
485
+ // UsersView fallback). This is NOT the full user list — just the cache.
486
+ const users = Array.from(userCache.values());
548
487
 
549
488
  return {
550
- loading,
489
+ loading: effectiveLoading,
551
490
  users,
491
+ hasAdminUsers,
552
492
  saveUser,
553
493
  createUser,
554
494
  resetPassword,
555
495
  deleteUser,
556
- roles,
557
- saveRole,
558
- deleteRole,
559
496
  isAdmin,
560
497
  allowDefaultRolesCreation: isAdmin,
561
- includeCollectionConfigPermissions: true,
562
498
  defineRolesFor,
563
499
  getUser,
564
500
  searchUsers,
565
501
  usersError,
566
- rolesError,
567
502
  bootstrapAdmin
568
503
  };
569
504
  }
@@ -9,7 +9,7 @@ import {
9
9
  UserInfo
10
10
  } from "../types";
11
11
 
12
- const STORAGE_KEY = "rebase_auth";
12
+ const STORAGE_KEY = "rebase_react_auth";
13
13
 
14
14
  // Buffer time before expiry to trigger refresh (2 minutes)
15
15
  const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1000;
@@ -113,12 +113,11 @@ export function useRebaseAuthController(
113
113
 
114
114
  // Configure API URL on mount
115
115
  useEffect(() => {
116
- if (client) {
117
- authApi.setApiUrl(client.baseUrl);
118
- } else if (apiUrl) {
119
- authApi.setApiUrl(apiUrl);
116
+ const url = client?.baseUrl || apiUrl;
117
+ if (url) {
118
+ authApi.setApiUrl(url);
120
119
  }
121
- }, [client, apiUrl]);
120
+ }, [client, client?.baseUrl, apiUrl]);
122
121
 
123
122
  const clearError = useCallback(() => {
124
123
  setAuthProviderError(null);
@@ -281,7 +280,7 @@ export function useRebaseAuthController(
281
280
  // Install token getter onto client
282
281
  useEffect(() => {
283
282
  if (client) {
284
- client.setAuthTokenGetter(async () => {
283
+ client.setAuthTokenGetter?.(async () => {
285
284
  try { return await getAuthToken(); } catch { return null; }
286
285
  });
287
286
  if (client.setOnUnauthorized) {
@@ -314,7 +313,7 @@ export function useRebaseAuthController(
314
313
  if (defineRolesFor) {
315
314
  const customRoles = await defineRolesFor(convertedUser);
316
315
  if (customRoles) {
317
- convertedUser = { ...convertedUser, roles: customRoles.map(r => r.id) };
316
+ convertedUser = { ...convertedUser, roles: customRoles };
318
317
  }
319
318
  }
320
319
 
@@ -480,7 +479,7 @@ export function useRebaseAuthController(
480
479
  const customRoles = await defineRolesFor(convertedUser);
481
480
  if (customRoles) {
482
481
  convertedUser = { ...convertedUser,
483
- roles: customRoles.map(r => r.id) };
482
+ roles: customRoles };
484
483
  }
485
484
  }
486
485
 
@@ -576,7 +575,7 @@ roles: customRoles.map(r => r.id) };
576
575
  const customRoles = await defineRolesFor(userToSet);
577
576
  if (customRoles) {
578
577
  userToSet = { ...userToSet,
579
- roles: customRoles.map(r => r.id) };
578
+ roles: customRoles };
580
579
  }
581
580
  }
582
581
 
@@ -620,11 +619,15 @@ roles: customRoles.map(r => r.id) };
620
619
  if (!isMountedRef.current) return;
621
620
  if (customRoles) {
622
621
  userToSet = { ...userToSet,
623
- roles: customRoles.map(r => r.id) };
622
+ roles: customRoles };
624
623
  }
625
624
  }
626
625
  } catch (meError: unknown) {
627
626
  if (!isMountedRef.current) return;
627
+ if (meError instanceof authApi.AuthApiError && (meError.code === "NOT_FOUND" || meError.code === "UNAUTHORIZED")) {
628
+ clearSessionAndSignOut();
629
+ return;
630
+ }
628
631
  userToSet = convertToUser(stored.user);
629
632
  }
630
633
 
@@ -744,10 +747,10 @@ roles: customRoles.map(r => r.id) };
744
747
  emailPasswordLogin: true,
745
748
  googleLogin: !!(props.googleClientId),
746
749
  registration: authConfig?.registrationEnabled ?? false,
747
- passwordReset: true,
750
+ passwordReset: authConfig?.passwordReset ?? false,
748
751
  sessionManagement: true,
749
752
  profileUpdate: true,
750
- emailVerification: false,
753
+ emailVerification: authConfig?.emailVerification ?? false,
751
754
  enabledProviders: authConfig?.enabledProviders ?? []
752
755
  }
753
756
  };
package/src/index.ts CHANGED
@@ -22,5 +22,5 @@ export { useBackendUserManagement } from "./hooks/useBackendUserManagement";
22
22
  export type { BackendUserManagementConfig, UserManagement } from "./hooks/useBackendUserManagement";
23
23
 
24
24
  // API utilities
25
- export { setApiUrl, getApiUrl, fetchAuthConfig, clearAuthConfigCache, AuthApiError } from "./api";
25
+ export { setApiUrl, getApiUrl, fetchAuthConfig, AuthApiError } from "./api";
26
26
  export type { AuthConfigResponse } from "./api";
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AuthController, Role, User } from "@rebasepro/types";
1
+ import { AuthController, User } from "@rebasepro/types";
2
2
 
3
3
  /**
4
4
  * Auth controller that extends the base AuthController
@@ -50,7 +50,13 @@ export type RebaseAuthController = AuthController & {
50
50
  */
51
51
  export interface RebaseAuthControllerProps {
52
52
  /** The Rebase Client instance */
53
- client?: any;
53
+ client?: {
54
+ baseUrl?: string;
55
+ resolveToken?: () => Promise<string | null>;
56
+ setAuthTokenGetter?: (getter: () => Promise<string | null>) => void;
57
+ setOnUnauthorized?: (handler: () => Promise<boolean>) => void;
58
+ ws?: { setAuthTokenGetter: (getter: () => Promise<string>) => void };
59
+ };
54
60
  /** Base URL of the backend API */
55
61
  apiUrl?: string;
56
62
  /** Google OAuth client ID (optional, enables Google login) */
@@ -58,7 +64,7 @@ export interface RebaseAuthControllerProps {
58
64
  /** Callback when user signs out */
59
65
  onSignOut?: () => void;
60
66
  /** Define roles for a user after login */
61
- defineRolesFor?: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
67
+ defineRolesFor?: (user: User) => Promise<string[] | undefined> | string[] | undefined;
62
68
  }
63
69
 
64
70
  /**