Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 10, 2026

Implements roadmap item 1.5: complete login-to-usage flow per CONSOLE_AUTH_PLAN.md. Replaces hardcoded { name: 'John Doe', role: 'admin' } with real auth context, adds route protection, and scaffolds system administration UI.

@object-ui/auth package (new)

  • AuthProvider / useAuth — React context + hook for auth state (user, session, signIn, signUp, signOut)
  • AuthGuard — Route guard with role-based access, loading fallback support
  • createAuthClient — Factory abstracting better-auth REST endpoints (/sign-in/email, /sign-up/email, /get-session, etc.)
  • createAuthenticatedFetch — Fetch wrapper injecting Bearer token from session into ObjectStackAdapter
  • LoginForm / RegisterForm / ForgotPasswordForm — Tailwind-styled form components
  • UserMenu — Authenticated user display with avatar/initials
  • getUserInitials — Shared utility for avatar fallback text

Console integration

  • Wrapped app with AuthProvider, added /login, /register, /forgot-password public routes
  • Protected /apps/:appName/* with AuthGuard fallback={<Navigate to="/login" />}
  • AppContent reads useAuth() for dynamic ExpressionProvider user context
  • AppSidebar footer now renders real user data + functional sign-out via useAuth()
  • Added vite aliases for @object-ui/auth, @object-ui/permissions, @object-ui/tenant

System administration pages

  • systemObjects.ts — Schema definitions for sys_user, sys_org, sys_role, sys_permission, sys_audit_log
  • CRUD pages: UserManagement, OrgManagement, RoleManagement (admin-gated)
  • Read-only AuditLogPage, editable ProfilePage
  • Routes mounted at /apps/:appName/system/*
// Auth integration pattern
<AuthProvider authUrl="/api/auth">
  <Route path="/login" element={<LoginPage />} />
  <Route path="/apps/:appName/*" element={
    <AuthGuard fallback={<Navigate to="/login" />}>
      <AppContent />
    </AuthGuard>
  } />
</AuthProvider>

// Token injection for DataSource
const authenticatedFetch = createAuthenticatedFetch(authClient);
new ObjectStackAdapter({ baseUrl: '/api/v1', fetch: authenticatedFetch });

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link

vercel bot commented Feb 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 10, 2026 3:17am
objectui-console Ready Ready Preview, Comment Feb 10, 2026 3:17am
objectui-storybook Ready Ready Preview, Comment Feb 10, 2026 3:17am

Request Review

Copilot AI and others added 4 commits February 10, 2026 02:58
…, AuthGuard, forms, and token injection

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…Guard, dynamic user context, and system admin pages

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…rove error handling in createAuthClient

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…nt to comply with rules of hooks

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add console authentication system implementation feat: Console Authentication System — @object-ui/auth package, route guards, system admin pages Feb 10, 2026
Copilot AI requested a review from hotlong February 10, 2026 03:10
@github-actions
Copy link

📦 Bundle Size Report

Package Size Gzipped
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 5.16KB 1.20KB
auth (ForgotPasswordForm.js) 4.00KB 1.44KB
auth (LoginForm.js) 4.20KB 1.45KB
auth (RegisterForm.js) 5.66KB 1.59KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (createAuthClient.js) 2.78KB 0.98KB
auth (createAuthenticatedFetch.js) 1.24KB 0.60KB
auth (index.js) 1.14KB 0.50KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 1.50KB 0.55KB
components (index.js) 1811.76KB 425.83KB
core (index.js) 0.70KB 0.31KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 22.92KB 5.60KB
fields (index.js) 87.61KB 17.17KB
i18n (i18n.js) 2.03KB 0.77KB
i18n (index.js) 1.65KB 0.66KB
i18n (provider.js) 2.88KB 0.99KB
layout (index.js) 14.61KB 4.51KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (index.js) 1.03KB 0.50KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (usePullToRefresh.js) 2.41KB 0.80KB
mobile (useResponsive.js) 0.71KB 0.42KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.11KB 0.87KB
permissions (evaluator.js) 4.00KB 1.23KB
permissions (index.js) 0.85KB 0.40KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 0.99KB 0.49KB
plugin-aggrid (AddressField-Bntpynvd.js) 3.05KB 0.76KB
plugin-aggrid (AgGridImpl-3Mmf2qrR.js) 7.04KB 2.33KB
plugin-aggrid (AutoNumberField-C1kBJaxh.js) 0.28KB 0.27KB
plugin-aggrid (FileField-BDwbJvor.js) 3.50KB 1.34KB
plugin-aggrid (FormulaField-BXNiyGoh.js) 0.52KB 0.38KB
plugin-aggrid (GeolocationField-Df3yYcM9.js) 4.46KB 1.50KB
plugin-aggrid (GridField-CcjQp4WM.js) 1.71KB 0.67KB
plugin-aggrid (LocationField-BIfN5QIq.js) 0.93KB 0.54KB
plugin-aggrid (MasterDetailField-CAEmxbIT.js) 3.86KB 1.17KB
plugin-aggrid (ObjectAgGridImpl-EjifM4aY.js) 919.77KB 203.53KB
plugin-aggrid (ObjectField-BpkQpIF-.js) 1.61KB 0.77KB
plugin-aggrid (QRCodeField-VCBewTDG.js) 3.38KB 1.23KB
plugin-aggrid (RichTextField-CyQwSi2C.js) 1.16KB 0.59KB
plugin-aggrid (SignatureField-Cr4tsEbj.js) 3.32KB 1.28KB
plugin-aggrid (SummaryField-CnEJ_GZI.js) 0.48KB 0.37KB
plugin-aggrid (UserField-DJjaVyrV.js) 2.44KB 0.93KB
plugin-aggrid (VectorField-cPYmcKnV.js) 0.79KB 0.44KB
plugin-aggrid (index-B87wd1E0.js) 19.95KB 5.03KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-ai (index.js) 25.45KB 6.36KB
plugin-calendar (index.js) 31.63KB 8.05KB
plugin-charts (AdvancedChartImpl-DFmeUY4Q.js) 124.69KB 25.93KB
plugin-charts (BarChart-C_I0OFbj.js) 542.77KB 135.30KB
plugin-charts (ChartImpl-B5LY4On3.js) 3.17KB 1.10KB
plugin-charts (index-DSBO2Kdy.js) 15.18KB 4.46KB
plugin-charts (index.js) 0.19KB 0.16KB
plugin-chatbot (index.js) 1140.69KB 333.05KB
plugin-dashboard (index.js) 138.23KB 36.00KB
plugin-designer (index.js) 43.02KB 9.48KB
plugin-detail (index.js) 1711.31KB 403.73KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 52.17KB 10.49KB
plugin-gantt (index.js) 163.81KB 38.60KB
plugin-grid (index.js) 45.16KB 12.17KB
plugin-kanban (KanbanEnhanced-DjadKL5D.js) 31.43KB 9.03KB
plugin-kanban (KanbanImpl-BHAT_ney.js) 5.28KB 1.91KB
plugin-kanban (index-BhdljdIe.js) 17.29KB 5.01KB
plugin-kanban (index.js) 0.27KB 0.17KB
plugin-kanban (sortable.esm-ZHwgFQIO.js) 71.42KB 18.98KB
plugin-list (index.js) 1725.65KB 407.20KB
plugin-map (index.js) 126.32KB 30.52KB
plugin-map (maplibre-gl-CNsW26De.js) 1418.32KB 302.53KB
plugin-markdown (MarkdownImpl-DufQ-eRU.js) 256.68KB 64.45KB
plugin-markdown (index-CrmE78vF.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-report (index.js) 59.91KB 11.45KB
plugin-timeline (index.js) 105.30KB 24.14KB
plugin-view (index.js) 47.44KB 12.12KB
plugin-workflow (index.js) 30.88KB 7.14KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 3.28KB 1.30KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
tenant (TenantContext.js) 0.31KB 0.25KB
tenant (TenantGuard.js) 1.04KB 0.43KB
tenant (TenantProvider.js) 2.76KB 0.98KB
tenant (TenantScopedQuery.js) 0.77KB 0.44KB
tenant (index.js) 0.75KB 0.38KB
tenant (resolver.js) 2.64KB 0.76KB
tenant (useTenant.js) 0.50KB 0.32KB
tenant (useTenantBranding.js) 0.62KB 0.39KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 1.12KB 0.49KB
types (layout.js) 0.20KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (tenant.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB
types (workflow.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@hotlong hotlong marked this pull request as ready for review February 10, 2026 03:20
Copilot AI review requested due to automatic review settings February 10, 2026 03:20
@hotlong hotlong merged commit 9a78e9c into main Feb 10, 2026
13 of 14 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the Console “login-to-usage” flow by introducing a new @object-ui/auth workspace package (provider/hook/guard + basic auth forms), wiring the Console app to use auth context + guarded routes, and scaffolding initial system administration pages under /apps/:appName/system/*.

Changes:

  • Added @object-ui/auth package: AuthProvider/useAuth, AuthGuard, REST client helpers, and basic login/register/forgot-password UI components.
  • Integrated auth into apps/console routing and sidebar user display/sign-out.
  • Added initial “System” pages and object definitions scaffolding for admin/profile/audit areas.

Reviewed changes

Copilot reviewed 27 out of 28 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds workspace link for @object-ui/auth and updates lock snapshots accordingly.
packages/auth/package.json Defines new @object-ui/auth package build/test/lint metadata and exports.
packages/auth/tsconfig.json Adds package-level TS build config consistent with other workspace packages.
packages/auth/src/index.ts Public API exports for the new auth package.
packages/auth/src/types.ts Defines auth domain types + getUserInitials helper.
packages/auth/src/AuthContext.ts Declares the auth React context contract.
packages/auth/src/AuthProvider.tsx Implements auth state management + context provider wiring.
packages/auth/src/useAuth.ts Hook to consume the auth context.
packages/auth/src/AuthGuard.tsx Route/content guard component based on auth/role state.
packages/auth/src/createAuthClient.ts REST client factory for auth endpoints.
packages/auth/src/createAuthenticatedFetch.ts Fetch wrapper to inject session bearer token.
packages/auth/src/LoginForm.tsx Tailwind-styled login form consuming useAuth().
packages/auth/src/RegisterForm.tsx Tailwind-styled registration form consuming useAuth().
packages/auth/src/ForgotPasswordForm.tsx Tailwind-styled forgot password flow consuming useAuth().
packages/auth/src/UserMenu.tsx Headless-ish user info/actions block consuming useAuth().
apps/console/package.json Adds @object-ui/auth dependency to the Console app.
apps/console/vite.config.ts Adds Vite aliases for @object-ui/auth (and related future packages).
apps/console/src/App.tsx Wraps Console in AuthProvider, adds public auth routes, guards app routes, adds system routes, and feeds auth user into ExpressionProvider.
apps/console/src/components/AppSidebar.tsx Replaces hardcoded user info with useAuth() + sign-out integration.
apps/console/src/pages/LoginPage.tsx Adds login page wrapper around LoginForm.
apps/console/src/pages/RegisterPage.tsx Adds register page wrapper around RegisterForm.
apps/console/src/pages/ForgotPasswordPage.tsx Adds forgot-password page wrapper around ForgotPasswordForm.
apps/console/src/pages/system/systemObjects.ts Adds system object metadata scaffolding (users/orgs/roles/permissions/audit).
apps/console/src/pages/system/UserManagementPage.tsx Adds initial user management scaffold page.
apps/console/src/pages/system/OrgManagementPage.tsx Adds initial org management scaffold page.
apps/console/src/pages/system/RoleManagementPage.tsx Adds initial role management scaffold page.
apps/console/src/pages/system/AuditLogPage.tsx Adds initial audit log scaffold page.
apps/console/src/pages/system/ProfilePage.tsx Adds initial profile edit scaffold page.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +11 to +21
/**
* Options for creating an authenticated adapter.
*/
export interface AuthenticatedAdapterOptions {
/** Base URL for the ObjectStack API */
baseUrl: string;
/** Auth client to get session tokens from */
authClient: AuthClient;
/** Additional adapter options */
[key: string]: unknown;
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthenticatedAdapterOptions is exported but not used by createAuthenticatedFetch (which only accepts authClient). This adds API surface without functionality. Either remove this interface or update createAuthenticatedFetch to accept an options object and use the fields (e.g., to scope auth injection to a base URL).

Copilot uses AI. Check for mistakes.
Comment on lines +212 to +215
<Route path="system/users" element={<UserManagementPage />} />
<Route path="system/organizations" element={<OrgManagementPage />} />
<Route path="system/roles" element={<RoleManagementPage />} />
<Route path="system/audit-log" element={<AuditLogPage />} />
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System admin routes are mounted without any admin role guard, so any authenticated user can navigate to /apps/:appName/system/users|organizations|roles. The pages only hide the “Add …” button, which doesn’t meet the “admin-gated” intent and is a security gap. Wrap the admin-only routes (users/orgs/roles, possibly audit-log) in an AuthGuard requiredRoles={["admin"]} (or a permissions-based guard) and provide an explicit access-denied fallback.

Suggested change
<Route path="system/users" element={<UserManagementPage />} />
<Route path="system/organizations" element={<OrgManagementPage />} />
<Route path="system/roles" element={<RoleManagementPage />} />
<Route path="system/audit-log" element={<AuditLogPage />} />
<Route
path="system/users"
element={
<AuthGuard
requiredRoles={["admin"]}
fallback={
<Empty>
<EmptyTitle>Access denied</EmptyTitle>
</Empty>
}
>
<UserManagementPage />
</AuthGuard>
}
/>
<Route
path="system/organizations"
element={
<AuthGuard
requiredRoles={["admin"]}
fallback={
<Empty>
<EmptyTitle>Access denied</EmptyTitle>
</Empty>
}
>
<OrgManagementPage />
</AuthGuard>
}
/>
<Route
path="system/roles"
element={
<AuthGuard
requiredRoles={["admin"]}
fallback={
<Empty>
<EmptyTitle>Access denied</EmptyTitle>
</Empty>
}
>
<RoleManagementPage />
</AuthGuard>
}
/>
<Route
path="system/audit-log"
element={
<AuthGuard
requiredRoles={["admin"]}
fallback={
<Empty>
<EmptyTitle>Access denied</EmptyTitle>
</Empty>
}
>
<AuditLogPage />
</AuthGuard>
}
/>

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +15
export function ProfilePage() {
const { user, updateUser, isLoading } = useAuth();
const [name, setName] = useState(user?.name ?? '');
const [saved, setSaved] = useState(false);
const [error, setError] = useState<string | null>(null);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name state is initialized from user?.name only on the first render. Because user is typically loaded asynchronously, the input can stay empty even after user becomes available, and saving would overwrite the profile name with an empty string. Sync the local name state when user changes (e.g., via useEffect) or derive the input value directly from user until the user edits it.

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +55
/** Token expiry timestamp */
expiresAt?: Date;
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthSession.expiresAt is typed as a Date, but createAuthClient returns raw response.json() data without converting strings/numbers to Date instances. This makes the runtime value inconsistent with the declared type. Consider changing the type to a wire-friendly format (e.g., ISO string / epoch ms) or parsing it into a Date in createAuthClient.

Suggested change
/** Token expiry timestamp */
expiresAt?: Date;
/** Token expiry timestamp (ISO 8601 string from the auth server) */
expiresAt?: string;

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +52
return async (input: RequestInfo | URL, init?: RequestInit) => {
const session = await authClient.getSession();
const headers = new Headers(init?.headers);
if (session?.session?.token) {
headers.set('Authorization', `Bearer ${session.session.token}`);
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createAuthenticatedFetch calls authClient.getSession() for every outgoing request. With the provided createAuthClient, getSession() performs a network request, which can create significant overhead (and added latency) for chatty adapters. Consider caching the session/token in memory (with invalidation on auth state changes) or accepting a synchronous token getter sourced from AuthProvider state.

Suggested change
return async (input: RequestInfo | URL, init?: RequestInit) => {
const session = await authClient.getSession();
const headers = new Headers(init?.headers);
if (session?.session?.token) {
headers.set('Authorization', `Bearer ${session.session.token}`);
}
// Simple in-memory cache to avoid calling getSession() on every request.
// This keeps the API surface the same while reducing network overhead.
const CACHE_TTL_MS = 60_000; // 60 seconds
let cachedSession: Awaited<ReturnType<AuthClient['getSession']>> | null = null;
let lastSessionFetch = 0;
const getCachedSession = async () => {
const now = Date.now();
if (!cachedSession || now - lastSessionFetch > CACHE_TTL_MS) {
cachedSession = await authClient.getSession();
lastSessionFetch = now;
}
return cachedSession;
};
return async (input: RequestInfo | URL, init?: RequestInit) => {
const session = await getCachedSession();
const headers = new Headers(init?.headers);
if (session?.session?.token) {
headers.set('Authorization', `Bearer ${session.session.token}`);
}

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +66
async function request<T>(path: string, options?: RequestInit): Promise<T> {
const url = `${baseURL}${path}`;
const response = await fetchFn(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
credentials: 'include',
});

if (!response.ok) {
const body = await response.json().catch(() => null);
const message = (body && typeof body === 'object' && 'message' in body)
? String(body.message)
: `Auth request failed with status ${response.status}`;
throw new Error(message);
}

return response.json();
}

return {
async signIn(credentials: SignInCredentials) {
return request<{ user: AuthUser; session: AuthSession }>('/sign-in/email', {
method: 'POST',
body: JSON.stringify(credentials),
});
},

async signUp(data: SignUpData) {
return request<{ user: AuthUser; session: AuthSession }>('/sign-up/email', {
method: 'POST',
body: JSON.stringify(data),
});
},

async signOut() {
await request('/sign-out', { method: 'POST' });
},
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request() always calls response.json() on success. Several client methods (signOut, forgotPassword, resetPassword) conceptually return void, so this will throw if those endpoints respond with 204 No Content or any non-JSON body. Consider handling empty bodies (e.g., check status/Content-Length/Content-Type and skip JSON parsing) or splitting into requestJson vs requestVoid.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +47
/**
* Create an auth client instance.
*
* This factory creates an abstraction layer over the authentication provider.
* It is designed to work with better-auth but can be adapted to any auth backend
* that exposes standard REST endpoints for sign-in, sign-up, sign-out, and session management.
*
* @example
* ```ts
* const authClient = createAuthClient({ baseURL: '/api/auth' });
* const { user, session } = await authClient.signIn({ email, password });
* ```
*/
export function createAuthClient(config: AuthClientConfig): AuthClient {
const { baseURL, fetchFn = fetch } = config;

async function request<T>(path: string, options?: RequestInit): Promise<T> {
const url = `${baseURL}${path}`;
const response = await fetchFn(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
credentials: 'include',
});

if (!response.ok) {
const body = await response.json().catch(() => null);
const message = (body && typeof body === 'object' && 'message' in body)
? String(body.message)
: `Auth request failed with status ${response.status}`;
throw new Error(message);
}

return response.json();
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package introduces critical auth flows (REST client, provider state machine, guard logic) but currently has no unit tests. Since the repo uses Vitest broadly, consider adding tests covering: createAuthClient error/message extraction, handling of empty/non-JSON responses, AuthProvider session bootstrapping, and AuthGuard role checks.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +23
export interface AuthGuardProps {
/** Content to render when user is not authenticated */
fallback?: React.ReactNode;
/** Required roles (user must have at least one) */
requiredRoles?: string[];
/** Required permissions (user must have all) */
requiredPermissions?: string[];
/** Content to render when loading */
loadingFallback?: React.ReactNode;
/** Children to render when authenticated */
children: React.ReactNode;
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requiredPermissions is declared in the props but never enforced. This is misleading for consumers and can result in routes/components being treated as protected when they aren’t. Either implement permission checks (e.g., by integrating with the permissions system or accepting a hasPermission callback) or remove the prop from AuthGuardProps.

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +122
/** Path to redirect to when not authenticated */
redirectTo?: string;
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redirectTo is part of AuthProviderConfig, but AuthProvider doesn’t read or apply it anywhere. If this is meant to be the canonical redirect target for unauthenticated users, wire it into AuthGuard usage (or expose it via context); otherwise remove it to avoid a “dead” config option.

Suggested change
/** Path to redirect to when not authenticated */
redirectTo?: string;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants