---
name: frontend-auth
description: Scaffold the per-project useAuth hook + PermissionGuard component
group: development.frontend
cli: cli/scaffold-frontend-auth
allowed-tools: [Read, Glob, Grep, Bash]  # Bash: CLI invocation
---

# Frontend auth scaffold

Generates the canonical authentication primitives every SmartStack-generated
React app needs to gate UI by permission :

- `src/business/auth/useAuth.ts` — Zustand-backed hook that loads
  `/api/auth/me` once at mount, exposes `{ user, permissions, isAuthenticated,
  hasPermission(path) }`. The `hasPermission` is a strict
  `permissions.includes(path)` match — the path shape is whatever the DB
  seed emits, so the same string flows BA → PRD → DB → JSX literal →
  hasPermission lookup with no transformation in the pipeline.

- `src/components/auth/PermissionGuard.tsx` — wraps children, renders
  `null` when the auth store is still loading or when `hasPermission(path)
  === false`. Optional `fallback?: ReactNode` for a placeholder.

## Why a dedicated CLI

The pages emitted by `scaffold-component` reference `<PermissionGuard
permission="...">` everywhere. Without this companion scaffold the
component does not exist (Phase 3a fails to build) or worse, it exists
as an unimplemented stub that always returns its children — letting any
authenticated user click every action regardless of role. A dedicated
CLI guarantees the hook + the guard are wired together, ship in a
known location, and stay overwritable on regeneration via the
`@customised` marker convention.

## Inputs

| Field | Type | Required | Notes |
|---|---|---|---|
| `projectPath` | string | yes | Absolute path to project root. |
| `appCode` | string | yes | App code (kebab-case) — used to derive web folder name (`web/{appCode}-web/`). |
| `force` | boolean | no | Overwrite even when content matches. Default `false`. |

## Outputs

| File | Idempotency |
|------|-------------|
| `web/{appCode}-web/src/business/auth/useAuth.ts` | Honours `// @customised` marker |
| `web/{appCode}-web/src/components/auth/PermissionGuard.tsx` | Honours `// @customised` marker |
| `web/{appCode}-web/src/business/auth/auth-store.ts` | Honours `// @customised` marker |

## When the runtime mounts

The `useAuth` hook fires `api.get('/api/auth/me')` once on the first
render of any consumer. The response shape it expects :

```json
{
  "user": { "id": "...", "email": "...", "displayName": "..." },
  "permissions": [
    "restauration.carte.dishes.read",
    "restauration.carte.dishes.create",
    "restauration.carte.dishes.allergens.read"
  ]
}
```

The backend endpoint must return `permissions` as an array of full path
strings matching whatever `*PermissionsSeedDataProvider.cs` seeded — 4
or 5 segments, lower-kebab case throughout.

## Anti-patterns

- ❌ Adding business logic to PermissionGuard. It does ONE thing: hide
  children when the user lacks the permission.
- ❌ Resolving permissions client-side via heuristics (e.g. inferring
  create from edit). The permission strings are the contract.
- ❌ Caching beyond the session. If the user's role changes, they must
  re-login to see the new gates.
