12 KiB
12 KiB
Tasks — complete-react-auth
Change ID: complete-react-auth
Package: @suaveplan/react-auth
Target path: packages/auth/react-auth/
Pre-flight: @suaveplan/types, @suaveplan/error, @suaveplan/events, @suaveplan/logger, @suaveplan/i18n, @suaveplan/webauthn, @suaveplan/oauth-client, @suaveplan/srp, @suaveplan/otp, @suaveplan/hibp, @suaveplan/password-strength, @suaveplan/password-generator, @suaveplan/react-ui — all ✅ on Verdaccio.
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create directory tree per
design.md§ File layout. - Write
package.jsonat0.1.0,"sideEffects": false,react ^19.0.0peer, every internal dep asworkspace:^, every external dep via catalog. - Write
tsconfig.json(extends@suaveplan/typescript-config/browser-react.json),vitest.config.ts(forks pool, fileParallelism false, istanbul 100/100/100/100),playwright.config.tsvia@suaveplan/testing-e2efactory. - Write
README.mdat package root — overview, install, quick-start (provider + sign-in form), Modules section linking every co-located.md.
2. Types + schemas + errors
src/types/auth-user.ts,auth-state.ts,auth-backend.ts,session.ts,idle-config.ts,permission-resolver.ts— each with sibling.md(≥ 200 words).src/schemas/schemas.ts— Zod schemas for every hook option bag (SignInOptions,SignUpOptions,UsePasskeyAutofillOptions,UseOAuthSignInOptions, …). Sibling.md,schemas.test.ts.src/errors/errors.ts—defineCodes("REACT_AUTH", [...])for the 29 codes indesign.md§ Error code namespace. ExportReactAuthError. Sibling.md,errors.test.tscovering: every code reachable from a production path; translation helpers preservecause.
3. Utilities
src/utils/compose-signals.ts(+.md,.test.ts) —AbortControllercomposition (any abort → composed abort).src/utils/is-ssr.ts(+.md,.test.ts).src/utils/announce.ts(+.md,.test.ts) — ARIA live-region helper (creates a single sharedaria-live="polite"node on first call).
4. Session — storage, broadcast, idle, refresh, forced-logout
src/session/storage/memory/memory.ts(+.md,.test.ts).src/session/storage/local/local.ts(+.md,.test.ts).src/session/storage/session/session.ts(+.md,.test.ts).src/session/broadcast-channel.ts(+.md,.test.ts) — typed envelope, factory injection, SSR no-op.src/session/auto-refresh.ts(+.md,.test.ts) — schedule via injectedClock; cancels on every state update.src/session/idle-timer.ts(+.md,.test.ts) — pointer/key/visibility listeners; debounced;onWarn/onTimeout.src/session/forced-logout.ts(+.md,.test.ts) — broadcasts + clears storage synchronously.
5. Provider + context
src/auth-provider/auth-context.ts(+.md,.test.ts).src/auth-provider/use-auth-context.ts(+.md,.test.ts) — throwsREACT_AUTH.CONTEXT_MISSINGoutside provider.src/auth-provider/load-persisted-session.ts(+.md,.test.ts).src/auth-provider/auth-provider.tsx(+.md,.test.tsx,.stories.tsx) — wires backend + storage + broadcast + idle + auto-refresh; deterministic SSR render; effect-only DOM access.
6. Lifecycle hooks
src/hooks/use-auth/(+.md,.test.ts).src/hooks/use-user/(+.md,.test.ts).src/hooks/use-is-authenticated/(+.md,.test.ts).src/hooks/use-session/(+.md,.test.ts).src/hooks/use-sign-in/(+.md,.test.ts) — supports password, SRP, passkey result handoff.src/hooks/use-sign-up/(+.md,.test.ts).src/hooks/use-sign-out/(+.md,.test.ts) — broadcastssigned-out.src/hooks/use-refresh-token/(+.md,.test.ts).
7. Password hooks + components
src/hooks/use-change-password/(+.md,.test.ts).src/hooks/use-password-reset/(+.md,.test.ts) —request+confirm.src/components/password-strength-meter/(+.md,.test.tsx,.stories.tsx) — wrapspassword-strength.assessReactive; cancels on unmount.src/components/hibp-pwned-indicator/(+.md,.test.tsx,.stories.tsx) — debounced ≥ 500 ms; SSR no-op; injectedHibpClient.src/components/password-input/(+.md,.test.tsx,.stories.tsx) — show/hide, label association,aria-describedby, optional strength + HIBP slots.src/components/password-generator-button/(+.md,.test.tsx,.stories.tsx) — popover via@suaveplan/react-ui<Popover>; passphrase/chars/pin modes.
8. Passkey hooks + components
src/hooks/use-register-passkey/(+.md,.test.ts).src/hooks/use-authenticate-with-passkey/(+.md,.test.ts) — modal mediation.src/hooks/use-passkey-autofill/(+.md,.test.tsx) — folded from superseded autofill change; SSR-safe;isConditionalMediationAvailablefeature-detect; composedAbortController; stableabort.src/components/passkey-autofill-field/(+.md,.test.tsx,.stories.tsx) —<input autocomplete="username webauthn">wrapper; blur cancels when no credential delivered.src/components/passkey-list/(+.md,.test.tsx,.stories.tsx) — lists frombackend.listPasskeys; per-row revoke with accessible confirm dialog.
9. OAuth
src/oauth/provider-registry.ts(+.md,.test.ts) —register / get / has / list / unregister; descriptor includes label key + icon slot + scopes.src/oauth/popup.ts(+.md,.test.ts) — centred-window helper;postMessageorigin check; popup-blocked detection.src/oauth/redirect.ts(+.md,.test.ts) —sessionStoragereturn-state persist;window.location.assign.src/oauth/handle-callback.ts(+.md,.test.ts) — parses query, validates state, exchanges code, dispatches result; idempotent on remount.src/hooks/use-oauth-sign-in/(+.md,.test.ts) — popup + redirect modes; PKCE via@suaveplan/oauth-client.src/components/oauth-provider-button/(+.md,.test.tsx,.stories.tsx).
10. MFA
src/hooks/use-enroll-totp/(+.md,.test.ts) —begin+finish.src/hooks/use-verify-totp/(+.md,.test.ts).src/hooks/use-enroll-webauthn-second-factor/(+.md,.test.ts) — wrapsregisterwithauthenticatorSelection.authenticatorAttachment.src/hooks/use-recovery-codes/(+.md,.test.ts) —generate+verify.src/components/totp-enrollment-panel/(+.md,.test.tsx,.stories.tsx) — orchestrates secret display → caller-provided QR slot → code entry → recovery-codes reveal.src/components/recovery-codes-panel/(+.md,.test.tsx,.stories.tsx) — copy / download / acknowledge before dismiss.
11. Verification flows
src/hooks/use-email-verification/(+.md,.test.ts).src/hooks/use-phone-verification/(+.md,.test.ts).src/components/email-verification-view/(+.md,.test.tsx,.stories.tsx) — resend cooldown timer.src/components/phone-verification-view/(+.md,.test.tsx,.stories.tsx) —inputMode="numeric",autocomplete="one-time-code".
12. Guards
src/hooks/use-permission/(+.md,.test.ts).src/hooks/use-auth-guard/(+.md,.test.ts).src/components/auth-guard/(+.md,.test.tsx,.stories.tsx) —withAuthHOC alongside component.src/components/permission-gate/(+.md,.test.tsx,.stories.tsx).
13. Error boundary
src/components/auth-error-boundary/(+.md,.test.tsx,.stories.tsx) — catchesSuaveplanErrorwhose code startsREACT_AUTH.; renders i18n-keyed fallback; re-throws everything else.
14. i18n
src/i18n/en-gb/index.ts(+.md) — canonical catalogue with everyreact-auth.*key used in the package.src/i18n/cy-gb/index.ts(+.md) — full Welsh translation.src/i18n/zh-cn/index.ts(+.md) — full Simplified Chinese translation.src/i18n/parity.test.ts— asserts every locale has the same key set asen-GB; fails CI on drift.src/i18n/no-hardcoded-strings.test.ts— scanscomponents/**/*.tsxfor hardcoded user-visible strings; allowlist limited to test fixtures.
15. Index + subpath exports
src/index.ts— explicit named re-exports (rule 6a); namespace re-exports forerrorsandschemas.package.jsonexportsmap — one entry per subpath perproposal.md.- Type-only re-exports separated via
export { type X }.
16. Playwright E2E
e2e/fixtures/app.tsx— minimal test app wiring an in-memoryAuthBackend.e2e/password-sign-in.spec.ts,sign-up.spec.ts,password-reset.spec.ts,change-password.spec.ts.e2e/passkey-register.spec.ts,passkey-autofill.spec.ts,passkey-modal-authenticate.spec.ts(virtual-authenticator via Playwright CDP).e2e/oauth-popup.spec.ts,oauth-redirect.spec.ts— stubbed authorization endpoint.e2e/totp-enroll-verify.spec.ts,recovery-codes.spec.ts.e2e/email-verification.spec.ts,phone-verification.spec.ts.e2e/multi-tab-sync.spec.ts— two browser contexts share the same backend; sign-out in tab A propagates to tab B.e2e/idle-timeout.spec.ts— idle config firesonTimeoutafter configured ms; user is forced out.e2e/error-boundary.spec.ts— triggers aBACKEND_ERROR; verifies the boundary fallback renders.e2e/rtl-locale.spec.ts— renders with an RTL locale (e.g.ar) via the parent app; verifiesdir="rtl"propagates.e2e/accessibility.spec.ts—@axe-core/playwrightsweep across every canonical page; zero serious / critical violations.
17. Spec reconciliation (CLAUDE.md §13 step 0)
- Open
specs/react-auth/spec.md. Walk every### Requirementand#### Scenario. For each**Verified by:**path, confirm the test file exists and the test name matches verbatim. - Confirm every error code in
defineCodes("REACT_AUTH", …)is reached from a real production path (not just a registry entry).
18. Pre-publish gates
bunx turbo lint typecheck test --filter=@suaveplan/react-auth— green.bunx turbo test:coverage --filter=@suaveplan/react-auth— 100/100/100/100.bunx turbo build --filter=@suaveplan/react-auth— green.- Banned-pattern scan zero (
@deprecated|TODO|FIXME|ts-ignore|ts-expect-error|\.skip|\.only|console\.log). - Barrel-shape scan zero (
export *only insrc/index.tsnamespace forms). - Test-runner import scan zero (
from "vitest"/from "bun:test"outside the exempt allowlist). - Missing-
.mdscan zero. - Bare-version scan zero.
- Subpath export count matches feature-module count.
- React version check:
find . -path "*/node_modules/react/package.json"returns a single version.
19. Root-doc refresh
bun scripts/folder-reorg/generate-manifest.ts(refreshMANIFEST.md).bun scripts/generate-missing.ts(refreshMISSING.yml).- Edit
STATUS.md— move@suaveplan/react-authfrom–to ✅; add summary paragraph. - Edit
SUMMARY.md— add per-package paragraph (features, deps, decisions). bunx markdownlint-cli2 --fix— touched files.
20. Supersede existing change folders
mv openspec/changes/enterprise-react-auth openspec/changes/_superseded-narrow-deferrals-2026-05-17/enterprise-react-auth— fold under the existing supersede bucket.
21. Fold spec delta + archive
- Fold
specs/react-auth/spec.mdintoopenspec/specs/react-auth/spec.md(canonical home). mv openspec/changes/complete-react-auth openspec/archive/<yyyy-mm-dd>-complete-react-auth/.bun scripts/publish.ts --package @suaveplan/react-auth.