---
name: frontend-auth
description: Scaffold the per-project useAuth adapter + PermissionGuard
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` — thin ADAPTER over the package's
  `useAuth()` (`@atlashub/smartstack` AuthContext, mounted by the app shell's
  `AuthProvider`). The package is the SINGLE auth source — no parallel store,
  no duplicate `/api/auth/me` bootstrap. The adapter layers the house
  `hasPermission(path)` on top: super-admin `*`, strict match, subtree
  wildcard, and the **strip-leading-appCode pass** the package matcher does
  not have. Generated pages pass page-side 3-/4-segment paths
  (`{module}.{section}.{action}`) while grants are canonical 4-/5-segment
  (`{app}.…`) — importing the package hook DIRECTLY into a guard therefore
  fails every check for role-based users (only `*` super-admins pass).

- `src/components/auth/PermissionGuard.tsx` — wraps children, renders
  `null` while auth is loading or when `hasPermission(path) === false`.
  Optional `fallback?: ReactNode`; `page` prop renders the visible
  `permission-denied` block for page-level denials (UAT signal).


## Why a dedicated CLI

The pages emitted by `scaffold-component` reference `<PermissionGuard
permission="...">` everywhere, and `scaffold-ui-primitives`' RowActionsMenu
plus the FormPage "Me" shortcut import `@/business/auth/useAuth`.
Without this companion scaffold those imports resolve to NOTHING (TS2307,
Phase 3a fails to build). A dedicated CLI guarantees the two files ship in
the known locations and stay overwritable on regeneration via the
`@customised` marker convention. **It runs in Phase 3.0 of `/ba-develop`**,
alongside scaffold-theme / scaffold-layout / scaffold-ui-primitives.

## 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 |

## Permission path shapes

Page-side (what generated JSX passes): 3 segments `{module}.{section}.{action}`
(section-scoped) or 4 segments with a resource. Grant-side (what
`*PermissionsSeedDataProvider.cs` seeds and `/api/auth/me` returns): the
canonical 4-/5-segment path WITH the appCode prefix, plus `*` (super-admin)
and `prefix.*` subtree wildcards. The adapter's `hasPermission` bridges the
two; the strings themselves are the contract end-to-end.

## Anti-patterns

- ❌ Registering a placeholder component on a platform application/module
  componentKey (`administration`, `administration.<module>`). The SDK's
  DynamicRouter already renders `ApplicationHomePage` / `ModuleHomePage` for a bare
  application/module route, and a registered component ALWAYS wins over the generic
  one — so the placeholder BLANKS the page it was meant to protect. That is exactly
  what the removed `AdminModuleFallback` did to every `/administration/*` landing
  page (CLI ≤ 5.12). There is no MISSING_PAGE to guard against at that level either:
  `MissingComponentPage` is only reachable from a section or resource key.

- ❌ Importing the package `useAuth` directly into a permission guard — its
  matcher has no strip-leading-appCode pass; role-based users fail every
  page-side 3-seg check (invisible in dev as super-admin, fatal in UAT).
- ❌ Re-introducing a parallel auth store / a second `/api/auth/me` fetch.
  The package `AuthProvider` owns the session; the adapter only re-derives.
- ❌ 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.
