# 11. tRPC Foundation — Auth Context, Middleware, and Protected Procedures

meta:
  id: shieldai-unified-restructure-11
  feature: shieldai-unified-restructure
  priority: P0
  depends_on: [shieldai-unified-restructure-10]
  tags: [backend, trpc, auth, api]

objective:
- Establish the tRPC foundation in the unified monolith: router factory, context builder with auth, middleware for protected routes, and error handling. Replace the stub tRPC setup with a production-ready auth-aware API layer.

deliverables:
- `web/src/server/api/utils.ts` — tRPC initialization and helpers:
  - `t` object from `initTRPC.context<typeof createTRPCContext>().create()`
  - `createTRPCRouter` factory
  - `publicProcedure` — unauthenticated
  - `protectedProcedure` — requires valid JWT/session
  - `adminProcedure` — requires admin role
  - `rateLimitedProcedure` — with basic rate limiting middleware
- `web/src/server/api/trpc.ts` — Context builder:
  - `createTRPCContext({ req, res })` that extracts auth from:
    - Session cookie (SolidStart session)
    - `Authorization: Bearer <jwt>` header
    - `x-api-key` header (for service-to-service or extension calls)
  - Returns `{ db, user, session, apiKey }` in context
  - Uses Drizzle `db` instance from `web/src/server/db/index.ts`
- `web/src/server/api/root.ts` — Root router:
  - Imports all sub-routers
  - Exports `appRouter` and `AppRouter` type
- `web/src/routes/api/trpc/[trpc].ts` — tRPC API handler:
  - SolidStart API route that handles all tRPC requests
  - Uses `fetchRequestHandler` from `@trpc/server/adapters/fetch`
  - Wires `createTRPCContext`
- `web/src/lib/api.ts` — tRPC client:
  - Updates existing file to use correct `AppRouter` type
  - Adds auth token injection from localStorage/cookie
  - Handles unauthorized responses (401 → redirect to login)
- Auth utilities:
  - `web/src/server/auth/jwt.ts` — JWT sign/verify using `jose` or `jsonwebtoken`
  - `web/src/server/auth/session.ts` — Session creation and validation
  - `web/src/server/auth/password.ts` — Password hashing with `bcrypt` or `argon2`

steps:
1. Install dependencies in `web/`:
   - `@trpc/server`, `@trpc/client` (already present, verify versions match)
   - `jose` (modern JWT library, works in Edge runtime)
   - `bcryptjs` or `argon2` for password hashing
   - `zod` for input validation (already present via valibot, but standardize on one — recommend `zod` for wider ecosystem compatibility)
2. Create `web/src/server/auth/jwt.ts`:
   - `signJWT(payload, secret, expiresIn)`
   - `verifyJWT(token, secret)`
   - Use `jose` for Edge compatibility
3. Create `web/src/server/auth/password.ts`:
   - `hashPassword(password)` → async hash
   - `verifyPassword(password, hash)` → boolean
4. Create `web/src/server/api/trpc.ts` (context builder):
   - Parse cookies from request headers
   - Try session cookie first (SolidStart session)
   - Fall back to Bearer JWT
   - Fall back to x-api-key
   - Look up user in database via Drizzle
   - Return context with `user`, `session`, `db`, `apiKey`
5. Update `web/src/server/api/utils.ts`:
   - Initialize tRPC with context type
   - Define `publicProcedure`
   - Define `protectedProcedure` using middleware that checks `ctx.user` and throws `TRPCError.UNAUTHORIZED` if missing
   - Define `adminProcedure` that checks `ctx.user.role === 'admin'`
6. Update `web/src/server/api/root.ts`:
   - Keep `exampleRouter` for now
   - Add placeholder imports for future routers
7. Update `web/src/routes/api/trpc/[trpc].ts`:
   - Create SolidStart API route
   - Use `fetchRequestHandler` with `router: appRouter`, `createContext: createTRPCContext`
8. Update `web/src/lib/api.ts`:
   - Import `AppRouter` from `~/server/api/root`
   - Add `httpBatchLink` with `headers` function that reads auth token from cookie/localStorage
   - Add error link that handles 401 by redirecting
9. Test with a temporary protected route that returns `ctx.user`.

steps:
- Unit: Context builder returns anonymous user for unauthenticated requests
- Unit: Context builder returns full user for valid JWT
- Unit: `protectedProcedure` rejects unauthenticated requests with 401
- Unit: `adminProcedure` rejects non-admin users with 403
- Integration: tRPC client can call public and protected endpoints
- E2E: Login flow sets cookie/JWT and subsequent requests are authenticated

acceptance_criteria:
- [ ] `createTRPCContext` correctly builds context from session, JWT, or API key
- [ ] `publicProcedure` allows unauthenticated access
- [ ] `protectedProcedure` requires authentication and provides `ctx.user`
- [ ] `adminProcedure` requires admin role
- [ ] tRPC API handler responds correctly at `/api/trpc`
- [ ] Client library (`web/src/lib/api.ts`) connects and handles auth automatically
- [ ] JWT sign/verify works with `jose` library
- [ ] Password hashing uses bcrypt or argon2

validation:
- Create a temporary tRPC router with public and protected queries
- Call public query from browser → should succeed
- Call protected query without auth → should return 401
- Call protected query with valid JWT → should return user data
- Run `cd web && pnpm test` to verify auth unit tests

notes:
- The legacy Fastify auth middleware (`packages/api/src/middleware/auth.middleware.ts`) is the reference for auth logic. Port its JWT verification and API key handling.
- SolidStart sessions are cookie-based. The tRPC context should read the SolidStart session cookie and validate it.
- For the extension (task 27), API key auth will be important. Ensure `x-api-key` handling is robust.
- Consider using `lucia-auth` or `next-auth` SolidStart integration for session management, but a custom JWT approach is fine if it matches the legacy system.
- Standardize on `zod` for input validation across all tRPC routers. The current setup uses valibot — migrate to zod for consistency.
- Keep auth utilities in `web/src/server/auth/` separate from tRPC to allow reuse by other server code (e.g., background jobs).
