@checkstack/auth-frontend 0.0.4 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,177 @@
1
1
  # @checkstack/auth-frontend
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9faec1f: # Unified AccessRule Terminology Refactoring
8
+
9
+ This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
10
+
11
+ ## Changes
12
+
13
+ ### Core Infrastructure (`@checkstack/common`)
14
+
15
+ - Introduced `AccessRule` interface as the primary access control type
16
+ - Added `accessPair()` helper for creating read/manage access rule pairs
17
+ - Added `access()` builder for individual access rules
18
+ - Replaced `Permission` type with `AccessRule` throughout
19
+
20
+ ### API Changes
21
+
22
+ - `env.registerPermissions()` → `env.registerAccessRules()`
23
+ - `meta.permissions` → `meta.access` in RPC contracts
24
+ - `usePermission()` → `useAccess()` in frontend hooks
25
+ - Route `permission:` field → `accessRule:` field
26
+
27
+ ### UI Changes
28
+
29
+ - "Roles & Permissions" tab → "Roles & Access Rules"
30
+ - "You don't have permission..." → "You don't have access..."
31
+ - All permission-related UI text updated
32
+
33
+ ### Documentation & Templates
34
+
35
+ - Updated 18 documentation files with AccessRule terminology
36
+ - Updated 7 scaffolding templates with `accessPair()` pattern
37
+ - All code examples use new AccessRule API
38
+
39
+ ## Migration Guide
40
+
41
+ ### Backend Plugins
42
+
43
+ ```diff
44
+ - import { permissionList } from "./permissions";
45
+ - env.registerPermissions(permissionList);
46
+ + import { accessRules } from "./access";
47
+ + env.registerAccessRules(accessRules);
48
+ ```
49
+
50
+ ### RPC Contracts
51
+
52
+ ```diff
53
+ - .meta({ userType: "user", permissions: [permissions.read.id] })
54
+ + .meta({ userType: "user", access: [access.read] })
55
+ ```
56
+
57
+ ### Frontend Hooks
58
+
59
+ ```diff
60
+ - const canRead = accessApi.usePermission(permissions.read.id);
61
+ + const canRead = accessApi.useAccess(access.read);
62
+ ```
63
+
64
+ ### Routes
65
+
66
+ ```diff
67
+ - permission: permissions.entityRead.id,
68
+ + accessRule: access.read,
69
+ ```
70
+
71
+ ### Patch Changes
72
+
73
+ - 95eeec7: # Auto-login after credential registration
74
+
75
+ Users are now automatically logged in after successful registration when using the credential (email & password) authentication strategy.
76
+
77
+ ## Changes
78
+
79
+ ### Backend (`@checkstack/auth-backend`)
80
+
81
+ - Added `autoSignIn: true` to the `emailAndPassword` configuration in better-auth
82
+ - Users no longer need to manually log in after registration; a session is created immediately upon successful sign-up
83
+
84
+ ### Frontend (`@checkstack/auth-frontend`)
85
+
86
+ - Updated `RegisterPage` to use full page navigation after registration to ensure the session state refreshes correctly
87
+ - Updated `LoginPage` to use full page navigation after login to ensure fresh permissions state when switching between users
88
+
89
+ - Updated dependencies [9faec1f]
90
+ - Updated dependencies [f533141]
91
+ - @checkstack/auth-common@0.2.0
92
+ - @checkstack/common@0.2.0
93
+ - @checkstack/frontend-api@0.1.0
94
+ - @checkstack/ui@0.2.0
95
+
96
+ ## 0.1.0
97
+
98
+ ### Minor Changes
99
+
100
+ - 8e43507: # Teams and Resource-Level Access Control
101
+
102
+ This release introduces a comprehensive Teams system for organizing users and controlling access to resources at a granular level.
103
+
104
+ ## Features
105
+
106
+ ### Team Management
107
+
108
+ - Create, update, and delete teams with name and description
109
+ - Add/remove users from teams
110
+ - Designate team managers with elevated privileges
111
+ - View team membership and manager status
112
+
113
+ ### Resource-Level Access Control
114
+
115
+ - Grant teams access to specific resources (systems, health checks, incidents, maintenances)
116
+ - Configure read-only or manage permissions per team
117
+ - Resource-level "Team Only" mode that restricts access exclusively to team members
118
+ - Separate `resourceAccessSettings` table for resource-level settings (not per-grant)
119
+ - Automatic cleanup of grants when teams are deleted (database cascade)
120
+
121
+ ### Middleware Integration
122
+
123
+ - Extended `autoAuthMiddleware` to support resource access checks
124
+ - Single-resource pre-handler validation for detail endpoints
125
+ - Automatic list filtering for collection endpoints
126
+ - S2S endpoints for access verification
127
+
128
+ ### Frontend Components
129
+
130
+ - `TeamsTab` component for managing teams in Auth Settings
131
+ - `TeamAccessEditor` component for assigning team access to resources
132
+ - Resource-level "Team Only" toggle in `TeamAccessEditor`
133
+ - Integration into System, Health Check, Incident, and Maintenance editors
134
+
135
+ ## Breaking Changes
136
+
137
+ ### API Response Format Changes
138
+
139
+ List endpoints now return objects with named keys instead of arrays directly:
140
+
141
+ ```typescript
142
+ // Before
143
+ const systems = await catalogApi.getSystems();
144
+
145
+ // After
146
+ const { systems } = await catalogApi.getSystems();
147
+ ```
148
+
149
+ Affected endpoints:
150
+
151
+ - `catalog.getSystems` → `{ systems: [...] }`
152
+ - `healthcheck.getConfigurations` → `{ configurations: [...] }`
153
+ - `incident.listIncidents` → `{ incidents: [...] }`
154
+ - `maintenance.listMaintenances` → `{ maintenances: [...] }`
155
+
156
+ ### User Identity Enrichment
157
+
158
+ `RealUser` and `ApplicationUser` types now include `teamIds: string[]` field with team memberships.
159
+
160
+ ## Documentation
161
+
162
+ See `docs/backend/teams.md` for complete API reference and integration guide.
163
+
164
+ ### Patch Changes
165
+
166
+ - 97c5a6b: Fix Radix UI accessibility warning in dialog components by adding visually hidden DialogDescription components
167
+ - Updated dependencies [8e43507]
168
+ - Updated dependencies [97c5a6b]
169
+ - Updated dependencies [8e43507]
170
+ - @checkstack/ui@0.1.0
171
+ - @checkstack/auth-common@0.1.0
172
+ - @checkstack/common@0.1.0
173
+ - @checkstack/frontend-api@0.0.4
174
+
3
175
  ## 0.0.4
4
176
 
5
177
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/auth-frontend",
3
- "version": "0.0.4",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "exports": {
package/src/api.ts CHANGED
@@ -25,10 +25,10 @@ export interface Role {
25
25
  description?: string | null;
26
26
  isSystem?: boolean;
27
27
  isAssignable?: boolean; // False for anonymous role - not assignable to users
28
- permissions?: string[];
28
+ accessRules?: string[];
29
29
  }
30
30
 
31
- export interface Permission {
31
+ export interface AccessRuleEntry {
32
32
  id: string;
33
33
  description?: string;
34
34
  }
@@ -18,6 +18,7 @@ import {
18
18
  ConfirmationModal,
19
19
  Dialog,
20
20
  DialogContent,
21
+ DialogDescription,
21
22
  DialogHeader,
22
23
  DialogTitle,
23
24
  DialogFooter,
@@ -189,8 +190,8 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
189
190
  <Alert variant="info" className="mb-4">
190
191
  <AlertDescription>
191
192
  External applications use API keys to authenticate with the
192
- Checkstack API. The secret is only shown once when created—store it
193
- securely.
193
+ Checkstack API. The secret is only shown once when created—store
194
+ it securely.
194
195
  </AlertDescription>
195
196
  </Alert>
196
197
 
@@ -302,7 +303,7 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
302
303
 
303
304
  {!canManageApplications && (
304
305
  <p className="text-xs text-muted-foreground mt-4">
305
- You don't have permission to manage applications.
306
+ You don't have access to manage applications.
306
307
  </p>
307
308
  )}
308
309
  </CardContent>
@@ -346,6 +347,9 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
346
347
  <DialogTitle>
347
348
  Application Secret: {newSecretDialog.applicationName}
348
349
  </DialogTitle>
350
+ <DialogDescription className="sr-only">
351
+ Copy your application secret - it will only be shown once
352
+ </DialogDescription>
349
353
  </DialogHeader>
350
354
  <div className="space-y-4">
351
355
  <Alert variant="warning">
@@ -399,6 +403,9 @@ export const ApplicationsTab: React.FC<ApplicationsTabProps> = ({
399
403
  <DialogContent>
400
404
  <DialogHeader>
401
405
  <DialogTitle>Create Application</DialogTitle>
406
+ <DialogDescription className="sr-only">
407
+ Create a new external application with API key access
408
+ </DialogDescription>
402
409
  </DialogHeader>
403
410
  <div className="space-y-4">
404
411
  <div>
@@ -1,51 +1,54 @@
1
1
  import React, { useEffect, useState, useMemo } from "react";
2
2
  import { useSearchParams } from "react-router-dom";
3
- import {
4
- useApi,
5
- permissionApiRef,
6
- rpcApiRef,
7
- } from "@checkstack/frontend-api";
3
+ import { useApi, accessApiRef, rpcApiRef } from "@checkstack/frontend-api";
8
4
  import { PageLayout, useToast, Tabs, TabPanel } from "@checkstack/ui";
9
- import { authApiRef, AuthUser, Role, AuthStrategy, Permission } from "../api";
10
5
  import {
11
- permissions as authPermissions,
12
- AuthApi,
13
- } from "@checkstack/auth-common";
14
- import { Shield, Settings2, Users, Key } from "lucide-react";
6
+ authApiRef,
7
+ AuthUser,
8
+ Role,
9
+ AuthStrategy,
10
+ AccessRuleEntry,
11
+ } from "../api";
12
+ import { authAccess, AuthApi } from "@checkstack/auth-common";
13
+ import { Shield, Settings2, Users, Key, Users2 } from "lucide-react";
15
14
  import { UsersTab } from "./UsersTab";
16
15
  import { RolesTab } from "./RolesTab";
17
16
  import { StrategiesTab } from "./StrategiesTab";
18
17
  import { ApplicationsTab } from "./ApplicationsTab";
18
+ import { TeamsTab } from "./TeamsTab";
19
19
 
20
20
  export const AuthSettingsPage: React.FC = () => {
21
21
  const authApi = useApi(authApiRef);
22
22
  const rpcApi = useApi(rpcApiRef);
23
23
  const authClient = rpcApi.forPlugin(AuthApi);
24
- const permissionApi = useApi(permissionApiRef);
24
+ const accessApi = useApi(accessApiRef);
25
25
  const toast = useToast();
26
26
  const [searchParams, setSearchParams] = useSearchParams();
27
27
 
28
28
  const session = authApi.useSession();
29
29
 
30
30
  const [activeTab, setActiveTab] = useState<
31
- "users" | "roles" | "strategies" | "applications"
31
+ "users" | "roles" | "teams" | "strategies" | "applications"
32
32
  >("users");
33
33
  const [users, setUsers] = useState<(AuthUser & { roles: string[] })[]>([]);
34
34
  const [roles, setRoles] = useState<Role[]>([]);
35
- const [permissions, setPermissions] = useState<Permission[]>([]);
35
+ const [accessRuleEntries, setAccessRuleEntries] = useState<AccessRuleEntry[]>([]);
36
36
  const [strategies, setStrategies] = useState<AuthStrategy[]>([]);
37
37
  const [loading, setLoading] = useState(true);
38
38
 
39
- const canReadUsers = permissionApi.usePermission(
40
- authPermissions.usersRead.id
41
- );
39
+ const canReadUsers = accessApi.useAccess(authAccess.users.read);
42
40
 
43
41
  // Handle ?tab= URL parameters (from command palette)
44
42
  useEffect(() => {
45
43
  const tab = searchParams.get("tab");
46
44
 
47
- if (tab && ["users", "roles", "strategies", "applications"].includes(tab)) {
48
- setActiveTab(tab as "users" | "roles" | "strategies" | "applications");
45
+ if (
46
+ tab &&
47
+ ["users", "roles", "teams", "strategies", "applications"].includes(tab)
48
+ ) {
49
+ setActiveTab(
50
+ tab as "users" | "roles" | "teams" | "strategies" | "applications"
51
+ );
49
52
  }
50
53
 
51
54
  // Clear the URL params after processing
@@ -56,58 +59,41 @@ export const AuthSettingsPage: React.FC = () => {
56
59
  }
57
60
  }, [searchParams, setSearchParams]);
58
61
 
59
- const canManageUsers = permissionApi.usePermission(
60
- authPermissions.usersManage.id
61
- );
62
- const canCreateUsers = permissionApi.usePermission(
63
- authPermissions.usersCreate.id
64
- );
65
- const canReadRoles = permissionApi.usePermission(
66
- authPermissions.rolesRead.id
67
- );
68
- const canCreateRoles = permissionApi.usePermission(
69
- authPermissions.rolesCreate.id
70
- );
71
- const canUpdateRoles = permissionApi.usePermission(
72
- authPermissions.rolesUpdate.id
73
- );
74
- const canDeleteRoles = permissionApi.usePermission(
75
- authPermissions.rolesDelete.id
76
- );
77
- const canManageRoles = permissionApi.usePermission(
78
- authPermissions.rolesManage.id
79
- );
80
- const canManageStrategies = permissionApi.usePermission(
81
- authPermissions.strategiesManage.id
82
- );
83
- const canManageRegistration = permissionApi.usePermission(
84
- authPermissions.registrationManage.id
85
- );
86
- const canManageApplications = permissionApi.usePermission(
87
- authPermissions.applicationsManage.id
88
- );
89
-
90
- // Compute loading and permission states for PageLayout
91
- const permissionsLoading =
62
+ const canManageUsers = accessApi.useAccess(authAccess.users.manage);
63
+ const canCreateUsers = accessApi.useAccess(authAccess.users.create);
64
+ const canReadRoles = accessApi.useAccess(authAccess.roles.read);
65
+ const canCreateRoles = accessApi.useAccess(authAccess.roles.create);
66
+ const canUpdateRoles = accessApi.useAccess(authAccess.roles.update);
67
+ const canDeleteRoles = accessApi.useAccess(authAccess.roles.delete);
68
+ const canManageRoles = accessApi.useAccess(authAccess.roles.manage);
69
+ const canManageStrategies = accessApi.useAccess(authAccess.strategies);
70
+ const canManageRegistration = accessApi.useAccess(authAccess.registration);
71
+ const canManageApplications = accessApi.useAccess(authAccess.applications);
72
+ const canReadTeams = accessApi.useAccess(authAccess.teams.read);
73
+ const canManageTeams = accessApi.useAccess(authAccess.teams.manage);
74
+
75
+ const accessRulesLoading =
92
76
  loading ||
93
77
  canReadUsers.loading ||
94
78
  canReadRoles.loading ||
95
79
  canManageStrategies.loading ||
96
- canManageApplications.loading;
80
+ canManageApplications.loading ||
81
+ canReadTeams.loading;
97
82
 
98
- const hasAnyPermission =
83
+ const hasAnyAccess =
99
84
  canReadUsers.allowed ||
100
85
  canReadRoles.allowed ||
101
86
  canManageStrategies.allowed ||
102
- canManageApplications.allowed;
87
+ canManageApplications.allowed ||
88
+ canReadTeams.allowed;
103
89
 
104
- // Special case: if user is not logged in, show permission denied
105
- const isAllowed = session.data?.user ? hasAnyPermission : false;
90
+ // Special case: if user is not logged in, show access denied
91
+ const isAllowed = session.data?.user ? hasAnyAccess : false;
106
92
 
107
- // Compute visible tabs based on permissions
93
+ // Compute visible tabs based on access rules
108
94
  const visibleTabs = useMemo(() => {
109
95
  const tabs: Array<{
110
- id: "users" | "roles" | "strategies" | "applications";
96
+ id: "users" | "roles" | "teams" | "strategies" | "applications";
111
97
  label: string;
112
98
  icon: React.ReactNode;
113
99
  }> = [];
@@ -120,9 +106,15 @@ export const AuthSettingsPage: React.FC = () => {
120
106
  if (canReadRoles.allowed)
121
107
  tabs.push({
122
108
  id: "roles",
123
- label: "Roles & Permissions",
109
+ label: "Roles & Access Rules",
124
110
  icon: <Shield size={18} />,
125
111
  });
112
+ if (canReadTeams.allowed)
113
+ tabs.push({
114
+ id: "teams",
115
+ label: "Teams",
116
+ icon: <Users2 size={18} />,
117
+ });
126
118
  if (canManageStrategies.allowed)
127
119
  tabs.push({
128
120
  id: "strategies",
@@ -139,6 +131,7 @@ export const AuthSettingsPage: React.FC = () => {
139
131
  }, [
140
132
  canReadUsers.allowed,
141
133
  canReadRoles.allowed,
134
+ canReadTeams.allowed,
142
135
  canManageStrategies.allowed,
143
136
  canManageApplications.allowed,
144
137
  ]);
@@ -160,11 +153,11 @@ export const AuthSettingsPage: React.FC = () => {
160
153
  roles: string[];
161
154
  })[];
162
155
  const rolesData = await authClient.getRoles();
163
- const permissionsData = await authClient.getPermissions();
156
+ const accessRulesData = await authClient.getAccessRules();
164
157
  const strategiesData = await authClient.getStrategies();
165
158
  setUsers(usersData);
166
159
  setRoles(rolesData);
167
- setPermissions(permissionsData);
160
+ setAccessRuleEntries(accessRulesData);
168
161
  setStrategies(strategiesData);
169
162
  } catch (error: unknown) {
170
163
  const message =
@@ -188,7 +181,7 @@ export const AuthSettingsPage: React.FC = () => {
188
181
  return (
189
182
  <PageLayout
190
183
  title="Authentication Settings"
191
- loading={permissionsLoading}
184
+ loading={accessRulesLoading}
192
185
  allowed={isAllowed}
193
186
  >
194
187
  <Tabs
@@ -196,7 +189,7 @@ export const AuthSettingsPage: React.FC = () => {
196
189
  activeTab={activeTab}
197
190
  onTabChange={(tabId) =>
198
191
  setActiveTab(
199
- tabId as "users" | "roles" | "strategies" | "applications"
192
+ tabId as "users" | "roles" | "teams" | "strategies" | "applications"
200
193
  )
201
194
  }
202
195
  className="mb-6"
@@ -219,7 +212,7 @@ export const AuthSettingsPage: React.FC = () => {
219
212
  <TabPanel id="roles" activeTab={activeTab}>
220
213
  <RolesTab
221
214
  roles={roles}
222
- permissions={permissions}
215
+ accessRulesList={accessRuleEntries}
223
216
  userRoleIds={currentUserRoleIds}
224
217
  canReadRoles={canReadRoles.allowed}
225
218
  canCreateRoles={canCreateRoles.allowed}
@@ -229,6 +222,15 @@ export const AuthSettingsPage: React.FC = () => {
229
222
  />
230
223
  </TabPanel>
231
224
 
225
+ <TabPanel id="teams" activeTab={activeTab}>
226
+ <TeamsTab
227
+ users={users}
228
+ canReadTeams={canReadTeams.allowed}
229
+ canManageTeams={canManageTeams.allowed}
230
+ onDataChange={fetchData}
231
+ />
232
+ </TabPanel>
233
+
232
234
  <TabPanel id="strategies" activeTab={activeTab}>
233
235
  <StrategiesTab
234
236
  strategies={strategies}
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from "react";
2
- import { Link, useNavigate } from "react-router-dom";
2
+ import { Link } from "react-router-dom";
3
3
  import { LogIn, LogOut, AlertCircle } from "lucide-react";
4
4
  import {
5
5
  useApi,
@@ -38,7 +38,7 @@ import {
38
38
  } from "@checkstack/ui";
39
39
  import { authApiRef } from "../api";
40
40
  import { useEnabledStrategies } from "../hooks/useEnabledStrategies";
41
- import { usePermissions } from "../hooks/usePermissions";
41
+ import { useAccessRules } from "../hooks/useAccessRules";
42
42
  import { useAuthClient } from "../lib/auth-client";
43
43
  import { SocialProviderButton } from "./SocialProviderButton";
44
44
  import { useEffect } from "react";
@@ -47,7 +47,7 @@ export const LoginPage = () => {
47
47
  const [email, setEmail] = useState("");
48
48
  const [password, setPassword] = useState("");
49
49
  const [loading, setLoading] = useState(false);
50
- const navigate = useNavigate();
50
+
51
51
  const authApi = useApi(authApiRef);
52
52
  const rpcApi = useApi(rpcApiRef);
53
53
  const authRpcClient = rpcApi.forPlugin(AuthApi);
@@ -74,7 +74,8 @@ export const LoginPage = () => {
74
74
  if (error) {
75
75
  console.error("Login failed:", error);
76
76
  } else {
77
- navigate("/");
77
+ // Use full page navigation to ensure session/permissions state refreshes
78
+ globalThis.location.href = "/";
78
79
  }
79
80
  } finally {
80
81
  setLoading(false);
@@ -273,7 +274,7 @@ export const LogoutMenuItem = (_props: UserMenuItemsContext) => {
273
274
  export const LoginNavbarAction = () => {
274
275
  const authApi = useApi(authApiRef);
275
276
  const { data: session, isPending } = authApi.useSession();
276
- const { permissions, loading: permissionsLoading } = usePermissions();
277
+ const { accessRules, loading: accessRulesLoading } = useAccessRules();
277
278
  const authClient = useAuthClient();
278
279
  const [hasCredentialAccount, setHasCredentialAccount] =
279
280
  useState<boolean>(false);
@@ -295,7 +296,7 @@ export const LoginNavbarAction = () => {
295
296
  });
296
297
  }, [session?.user, authClient]);
297
298
 
298
- if (isPending || permissionsLoading || credentialLoading) {
299
+ if (isPending || accessRulesLoading || credentialLoading) {
299
300
  return <div className="w-20 h-9 bg-muted animate-pulse rounded-full" />;
300
301
  }
301
302
 
@@ -306,7 +307,7 @@ export const LoginNavbarAction = () => {
306
307
  );
307
308
  const hasBottomItems = bottomExtensions.length > 0;
308
309
  const menuContext: UserMenuItemsContext = {
309
- permissions,
310
+ accessRules,
310
311
  hasCredentialAccount,
311
312
  };
312
313
 
@@ -1,13 +1,9 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import { Link, useNavigate } from "react-router-dom";
2
+ import { Link } from "react-router-dom";
3
3
  import { AlertCircle } from "lucide-react";
4
4
  import { useApi, rpcApiRef } from "@checkstack/frontend-api";
5
5
  import { authApiRef } from "../api";
6
- import {
7
- AuthApi,
8
- authRoutes,
9
- passwordSchema,
10
- } from "@checkstack/auth-common";
6
+ import { AuthApi, authRoutes, passwordSchema } from "@checkstack/auth-common";
11
7
  import { resolveRoute } from "@checkstack/common";
12
8
  import {
13
9
  Button,
@@ -35,7 +31,7 @@ export const RegisterPage = () => {
35
31
  const [password, setPassword] = useState("");
36
32
  const [loading, setLoading] = useState(false);
37
33
  const [validationErrors, setValidationErrors] = useState<string[]>([]);
38
- const navigate = useNavigate();
34
+
39
35
  const authApi = useApi(authApiRef);
40
36
  const rpcApi = useApi(rpcApiRef);
41
37
  const authRpcClient = rpcApi.forPlugin(AuthApi);
@@ -87,7 +83,8 @@ export const RegisterPage = () => {
87
83
  if (res.error) {
88
84
  console.error("Registration failed:", res.error);
89
85
  } else {
90
- navigate("/");
86
+ // Use full page navigation to ensure session state refreshes in navbar
87
+ globalThis.location.href = "/";
91
88
  }
92
89
  } catch (error) {
93
90
  console.error("Registration failed:", error);