8.4 KiB
AGENTS.md — auth
Users collection + authentication use cases (sign-in, sign-up, sign-out). Provides the Users Payload collection, AuthenticationService, and tRPC procedures for authentication workflows.
Overview
@repo/auth owns: User/Session/Cookie domain models, auth-scoped errors, the IUsersRepository + IAuthenticationService interfaces, three use cases, three controllers, a real Payload-backed repository + service, and the tRPC authRouter. All procedures are mutations — there are no query builders.
Layer responsibilities
| Layer | Key files |
|---|---|
| entities/models | user.ts, session.ts, cookie.ts — Zod schemas + inferred types |
| entities/errors | auth.ts (AuthenticationError, UnauthenticatedError, UnauthorizedError), common.ts (InputParseError) |
| application/use-cases | sign-in.use-case.ts, sign-up.use-case.ts, sign-out.use-case.ts — factory functions + exported schemas |
| application/repositories | users.repository.interface.ts — IUsersRepository |
| application/services | authentication.service.interface.ts — IAuthenticationService |
| infrastructure/repositories | users.repository.ts (real Payload-backed), users.repository.mock.ts (in-memory) |
| infrastructure/services | authentication.service.ts (real Payload-backed), authentication.service.mock.ts (in-memory) |
| interface-adapters/controllers | sign-in.controller.ts, sign-up.controller.ts, sign-out.controller.ts — one file per use case |
| di | symbols.ts (AUTH_SYMBOLS), module.ts, container.ts, bind-production.ts |
| integrations/api | procedures.ts (authProcedure), router.ts (authRouter) |
| integrations/cms | collections/users.ts — Payload Users CollectionConfig |
| ui | src/ui/index.ts — placeholder (auth is mutations only; no query builders today) |
Public exports
| Subpath | Contents |
|---|---|
. |
User, Session, Cookie types; AuthenticationError, UnauthenticatedError, UnauthorizedError, InputParseError; SESSION_COOKIE; all use-case schemas + input/output types + IXUseCase aliases; IXController type aliases; AuthRouter type |
./ui |
Placeholder — extend here when auth gains React Query builders, never re-add to root |
./api |
authRouter (tRPC router) |
./cms |
Payload Users collection definition |
./di/bind-production |
bindProductionAuth(ctx: BindProductionContext) — swaps mock impls for real Payload-backed ones at app boot |
./di/bind-dev-seed |
bindDevSeedAuth(ctx: BindContext) — replaces the default empty mock with a populated one for dev / Storybook |
./di/container |
authContainer — the per-feature inversify container (consumed by e2e tests + production Payload event-tasks) |
./di/symbols |
AUTH_SYMBOLS — DI symbol registry (consumed by e2e tests + production Payload event-tasks) |
Use-case + controller patterns
See CLAUDE.md Key Conventions and docs/architecture/overview.md for the canonical factory templates.
Use cases
| Use case | Input schema | Output schema | Notes |
|---|---|---|---|
signInUseCase |
signInInputSchema — { username, password } |
signInOutputSchema — { session, cookie } |
Throws AuthenticationError on bad credentials |
signUpUseCase |
signUpInputSchema — { username, password, confirmPassword } with .refine |
signUpOutputSchema — { session, cookie } |
Throws AuthenticationError on taken username |
signOutUseCase |
signOutInputSchema — { sessionId } |
void (no xOutputSchema) |
Calls authenticationService.invalidateSession |
Controllers
| Controller | Presenter | Return type |
|---|---|---|
signInController |
presenter(value) { return value.cookie; } |
ReturnType<typeof presenter> (Cookie) |
signUpController |
presenter(value) { return value.cookie; } |
ReturnType<typeof presenter> (Cookie) |
signOutController |
none (void) | Promise<void> |
Controllers accept unknown input and safeParse with the use-case's xInputSchema, throwing InputParseError on failure.
Real Payload implementations (Plan 8)
UsersRepository(infrastructure/repositories/users.repository.ts) — callsgetPayload({ config })forgetUser,getUserByUsername, andcreateUser. ReceivesSanitizedConfigat constructor time.AuthenticationService(infrastructure/services/authentication.service.ts) — implementshashPasswordandverifyPasswordwith Node.jscrypto(pbkdf2). Three session-related methods (createSession,validateSession,invalidateSession) are deferred — they throwNotImplementedErrorwith a reference to refactor log §7. The mock (authentication.service.mock.ts) handles all test paths.
Errors → tRPC codes
| Error class | tRPC code | Thrown by |
|---|---|---|
InputParseError |
BAD_REQUEST |
controllers (safeParse failure) |
AuthenticationError |
UNAUTHORIZED |
sign-in / sign-up use cases |
UnauthenticatedError |
UNAUTHORIZED |
future session-guard middleware |
UnauthorizedError |
FORBIDDEN |
future authorization checks |
Defined in src/integrations/api/procedures.ts via authProcedure = t.procedure.use(defineErrorMiddleware([...])).
Tests
- Factories:
src/__factories__/user.factory.ts,src/__factories__/session.factory.ts - Contract suite:
src/__contracts__/users-repository.contract.ts— runs against mock and realUsersRepository - Unit tests: colocated
*.test.tsnext to each source file - Feature integration:
tests/sign-in-flow.feature.test.ts— full slice: tRPC caller → controller → use case → mock repo/service - R25 (output validation):
sign-in.use-case.test.tsandsign-up.use-case.test.tseach have a test that injects a malformed service mock and asserts.rejects.toBeInstanceOf(ZodError).signOutis void — no R25. - R26 (router error mapping):
router.test.tshasUNAUTHORIZEDon bad credentials andBAD_REQUESTon schema failure. - R27/R28 (presenter shape): sign-in and sign-up controller tests assert
result.name,result.value, etc. (Cookie shape), not the full{ session, cookie }use-case output.
pnpm test --filter @repo/auth
pnpm test --filter @repo/auth -- --watch
See docs/guides/tdd-workflow.md for the full cycle.
Directory structure
src/
entities/
models/
user.ts
session.ts
cookie.ts
errors/
auth.ts # AuthenticationError, UnauthenticatedError, UnauthorizedError
common.ts # InputParseError
application/
repositories/
users.repository.interface.ts
services/
authentication.service.interface.ts
use-cases/
sign-in.use-case.ts
sign-up.use-case.ts
sign-out.use-case.ts
infrastructure/
repositories/
users.repository.ts # real Payload-backed
users.repository.mock.ts
services/
authentication.service.ts # real (session methods deferred)
authentication.service.mock.ts
interface-adapters/
controllers/
sign-in.controller.ts
sign-up.controller.ts
sign-out.controller.ts
integrations/
api/
procedures.ts # authProcedure
router.ts # authRouter
cms/
collections/
users.ts
index.ts
di/
symbols.ts # AUTH_SYMBOLS
module.ts
container.ts
bind-production.ts
ui/
index.ts # placeholder
index.ts
__factories__/
user.factory.ts
session.factory.ts
__contracts__/
users-repository.contract.ts
tests/
sign-in-flow.feature.test.ts
What it must NOT import
- Any other feature package (
@repo/blog,@repo/media, etc.) - Any app package
@repo/core-api,@repo/core-cms,@repo/core-trpc,@repo/core-uidirectly; only@repo/core-shared
Note:
@repo/core-trpcand@repo/core-uiare optional packages scaffolded viapnpm turbo gen core-package trpc/ui. If not present, these constraints still apply to any future installation.
Cross-links
- ADR-012 (
docs/decisions/adr-012-lazar-conformance.md) — factory-style use cases, per-use-case controllers, file-naming conventions - ADR-013 (
docs/decisions/adr-013-input-output-unification.md) — schemas-in-use-case, presenter,./uisubpath, error middleware - Refactor logs:
docs/superpowers/refactor-logs/2026-05-05-lazar-pattern-conformance.md(Plan 8),docs/superpowers/refactor-logs/2026-05-06-input-output-unification.md(Plan 9)