Security checks belong at each boundary
Taskboard handles requests from browsers, messages from Stripe, job payloads from Postgres, and notifications from live connections. Each boundary has a different trust rule. One security middleware cannot answer every question.
For a task mutation, the server validates input, authenticates the session, verifies CSRF, checks workspace membership, checks the requested capability, and scopes the database operation to that workspace. Those checks form a sequence because each establishes information the next check needs.
Know what a control protects
Parameterized SQL separates query structure from user values. It does not prove that the query selected the correct tenant. A secure cookie protects how the browser transports a session token. It does not prove that the session has a project-editing role.
CORS describes which browser origins may read cross-origin responses. It does not prevent a command-line client from calling the API. CSRF addresses unwanted browser requests made with ambient cookies. Neither replaces server authorization.
Valid session: Maya
Requested workspace: another company's workspace
Expected result: denied before task data is returned
The failure case matters even when the requested task ID is valid. Every tenant-owned operation must retain its workspace condition.
Bound work and protect secrets
Taskboard bounds request bodies and applies rate limits to authentication endpoints. A login request also performs expensive password verification, so abuse affects CPU as well as the database. The reference stores fixed-window counters in Postgres, shared across API processes. The key combines an endpoint scope and a hash of the observed IP address. Correct proxy configuration matters because an incorrect trusted IP can group unrelated users or let callers evade the intended limit. Broader API admission limits remain an extension.
Production must serve the API over HTTPS and use appropriate cookie settings. Security headers help browsers apply restrictions. Dependency updates and input handling remain necessary. Express production security guidance.
Database credentials, SMTP passwords, Stripe secrets, and token-bearing payloads belong outside published course assets. Building the source browser must include code and safe examples only. The course is public reading material; real runtime configuration is private deployment data.
Database row-level security can add a second tenant boundary, but Taskboard uses application scoping and composite constraints. RLS is an extension requiring a trusted tenant context and carefully scoped database roles.
Read request controls in Read api/src/app.ts, Read api/src/security.ts, and Read api/src/tenancy.ts.
Identify the trust question at each boundary. Security controls have specific jobs and do not replace one another.
Does using Drizzle automatically prevent cross-tenant access?
No. Typed queries can still omit a workspace condition. Tenant scoping, constraints, and tests establish that boundary.