Files
2026-05-27 23:29:53 +01:00

7.9 KiB

Add @suaveplan/react-auth — core (Wave 1)

Package: @suaveplan/react-auth Status: PROPOSED Type: new-package

  • Spec delta: ./specs/react-auth/spec.md

Why

Genesis needs a React Better-Auth integration so downstream apps can authenticate users, gate routes, and check permissions. The upstream package at ../suaveplan/packages/react/react-auth/ ships a typed AuthClient wrapper around better-auth/client, an AuthProvider context, AuthGuard route gate, useAuthGuard/usePermission hooks, a betterAuthAdapter bridge, locale packs for en-GB/cy-GB/zh-CN, plus full page and form surfaces. Wave 1 ships the foundational core — adapter, client, context, hooks, guard, translations data, types — so that downstream packages (the parallel react-auth-whitelabel and consumer apps) can compile and authenticate today. Forms (Wave 2) and pages (Wave 3) layer on top of this core without changing it.

What changes

  • ADDED: AuthClient + createAuthClient(config) — typed wrapper around better-auth/client with x-site-domain header propagation.
  • ADDED: AuthProvider + useAuth — React context wrapping AuthClient with signIn/signUp/signOut/updateProfile/changePassword/forgotPassword/resetPassword/verifyEmail/resendVerificationEmail/deleteAccount/refreshSession actions and { user, session, isLoading, isAuthenticated, error } state.
  • ADDED: AuthGuard component — declarative requireAuth / requireGuest / requireVerified / allowedRoles gate with loadingComponent + fallback slots.
  • ADDED: useAuthGuard(options) hook — imperative react-router-dom redirect counterpart to AuthGuard.
  • ADDED: usePermission() hook — bitwise permission check (16 flags) against user.roles.
  • ADDED: transformSessionResponse / transformAuthResponse / transformGenericResponse / transformUpdateProfileResponse / isBetterAuthError / hasBetterAuthData — better-auth response adapter functions.
  • ADDED: Locale packs enGb / cyGb / zhCn and aggregate authTranslations (Wave 2 forms consume these).
  • ADDED: User / Session / Account / LoginCredentials / RegisterData / ResetPasswordData / ResetPasswordCredentials / UpdateProfileData / ChangePasswordData / AuthResponse / AuthError (auth.types.ts) and Site / UserSettings / BankAccount / Friend / Group / Post / Message / Notification / Event / Role / DeviceToken / PushNotification (crud.types.ts).
  • ADDED: REACT_AUTH_CODES registry (10 stable codes) + ReactAuthError base class + 10 typed subclasses.
  • ADDED: OTel instruments under meter suaveplan.react-auth — counters for sign-in/sign-up/sign-out/guard-allow/guard-deny, histogram for session-fetch duration.

Out of scope

  • Form components (LoginForm, RegisterForm, ForgotPasswordForm, ResetPasswordForm, EmailVerifyForm) — deferred to Wave 2.
  • Page components (Login, Register, ForgotPassword, ResetPassword, EmailVerify, Profile) and PermissionGate — deferred to Wave 3.

Excluded from this migration

  • Form components (login-form, register-form, forgot-password-form, reset-password-form, email-verify-form) — Deferred to Wave 2 — scope-bounded migration to keep ship velocity at 100/100/100/100 quality.
  • Page components (login, register, forgot-password, reset-password, email-verify, profile) and PermissionGate — Deferred to Wave 3 — scope-bounded migration to keep ship velocity at 100/100/100/100 quality.

Source mapping

This change derives from ../suaveplan/enterprise/react-auth.md and ../suaveplan/packages/react/react-auth/src/.

Requirement Source lines Notes
AuthClient wraps better-auth/client client/auth-client.ts:1-237 Reworded: @ts-expect-error on forgetPassword replaced with conditional dispatch through an optional-method lookup, removing the suppression.
AuthProvider context + 11 actions context/auth-context.tsx:1-435 Verbatim semantics; replaced loose (error as Error) casts with instanceof Error ? error : new Error(String(error)) to satisfy useThrowOnlyError.
AuthGuard declarative gate components/auth-guard/auth-guard.tsx:1-69 Verbatim.
useAuthGuard imperative gate hooks/use-auth-guard.ts:1-86 Verbatim.
usePermission bitwise hook hooks/use-permission.ts:1-88 Verbatim.
betterAuthAdapter response transforms lib/better-auth-adapter.ts:1-215 Verbatim; any payloads narrowed via unknown + structural type-guards.
Locale packs (en-GB, cy-GB, zh-CN) translations/{en-gb,cy-gb,zh-cn}.ts Verbatim string tables.
Auth + CRUD types types/{auth.types,crud.types}.ts Verbatim shapes.

Requirements with no source line

Requirement Justification
REACT_AUTH_CODES error-code registry + ReactAuthError subclasses Genesis-wide invariant (@suaveplan/error defineCodes); upstream threw plain Error. Audit enterprise/react-auth.md §3 flagged loss of programmatic handling and OTel tagging.
OTel instruments under suaveplan.react-auth Genesis-wide invariant (every package emits structured metrics).
Zod validation at createAuthClient config + useAuthGuard options + AuthGuardProps boundary Genesis rule §0a — Zod boundary on every public option bundle.
FetchLike injection seam at AuthClient construction Required by the testing rule "no mocks; real services or injectable boundaries".

Verification approach

  • src/client/auth-client.test.ts — covers AuthClient construction, siteDomain header propagation, every wrapper method's call shape, session-list date normalisation.
  • src/lib/better-auth-adapter.test.ts — covers all 6 transform functions over success/error/empty payloads.
  • src/context/auth-context.test.tsx — covers AuthProvider initial-session, all 11 actions, error state, skipInitialCheck, onAuthStateChange notification, unmount cleanup, useAuth outside-provider throw.
  • src/hooks/use-auth-guard.test.tsx — covers all 4 redirect paths (auth, guest, verified, role) + canAccess matrix + loading bail-out.
  • src/hooks/use-permission.test.tsx — covers hasPermission / hasAnyPermission / hasAllPermissions / getPermissionNames with string & numeric inputs, unauthenticated bail-outs.
  • src/components/auth-guard/auth-guard.test.tsx — covers loading state, every grant/deny branch, custom loadingComponent + fallback slots, multi-child rendering.
  • src/components/auth-provider/auth-provider.test.tsx — covers the re-export surface compiles to the canonical AuthProvider and type re-exports.
  • src/types/auth.types.test.ts + src/types/crud.types.test.ts — expectTypeOf shape pins for every exported interface.
  • src/translations/translations.test.ts — locale-pack key-parity across en-gb / cy-gb / zh-cn, default export shape.
  • src/error-codes/error-codes.test.ts — every code thrown by at least one production path; error name + code field round-trip.
  • src/observability/react-auth-metrics.test.ts — counters + histogram emit through a real in-memory MeterProvider.

Migration impact

  • ../suaveplan/packages/react/react-auth/src/components/auth-provider/auth-provider.types.ts imports PartialResources + SupportedLanguage from @suaveplan/react-translate. Wave 1 does not consume these types at runtime (the legacy AuthProviderProps.translationConfig is a Wave-2 form concern); they are re-declared as local TranslationConfig { defaultLanguage?: string; resources?: Record<string, unknown> } so this Wave-1 surface compiles without an @suaveplan/react-i18n dependency. Wave 2 will tighten the type when it wires useTranslation.

Impact

Affected dependents

No dependents yet — this is a new package landing on a clean genesis branch. Verified by bunx turbo ls --filter=...@suaveplan/react-auth (empty downstream list).