113 lines
5.9 KiB
Markdown
113 lines
5.9 KiB
Markdown
# 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).
|