fix(auth): derive clientIp server-side, drop it from sign-in input

clientIp was part of the public signInInputSchema, so any client could
spoof its own rate-limit bucket or dodge IP throttling entirely (audit
finding B2). The schema no longer carries it (strict parsing rejects it
with BAD_REQUEST); instead the web-next tRPC fetch adapter derives it in
createTrpcContext from x-forwarded-for (first hop) / x-real-ip — trust
caveat documented — and the router threads ctx.clientIp to the
controller as a second, server-only argument typed outside the input
schema (SignInRequestContext).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 17:28:56 +02:00
parent bae2686832
commit b66759a1ab
8 changed files with 207 additions and 14 deletions

View File

@@ -1,12 +1,15 @@
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@repo/core-api";
import { createTrpcContext } from "@repo/core-shared/trpc/context";
const handler = async (req: Request) => {
return fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => ({}),
// Threads server-derived fields (clientIp from proxy headers — see the
// trust caveat in core-shared/trpc/context.ts) into every procedure (B2).
createContext: () => createTrpcContext(req),
});
};