Files
agentic-dev/packages/auth/src/interface-adapters/controllers/sign-in.controller.ts
Danijel Martinek ac0bf80eca fix(auth): port server-side session revocation and sign-in hardening
Ports the upstream auth audit fixes onto the kept auth feature:

- revoke sessions server-side via an in-memory jti denylist (B5):
  createSession embeds the session id as the JWT jti, invalidateSession
  denylists it for the max token lifetime, validateSession rejects
  denylisted and jti-less (fail-closed) tokens; constant-time signature
  comparison (B4). Adds session-denylist.ts + test.
- cover signToken/verifyToken/validateSession crypto paths without a
  running Payload by stubbing the payload module (B8).
- derive clientIp server-side from trusted proxy headers and drop it from
  the public sign-in input schema; thread it as a server-only request
  context argument so a client can no longer spoof its rate-limit bucket
  (B2).
- declare the auth-injected email (and displayName) in the users
  collection-level DSR pii map so Art. 15 export and Art. 17 soft delete
  cover them (A5). Adapted to our collection set (no username field).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 23:53:43 +02:00

35 lines
1021 B
TypeScript

import { InputParseError } from "../../entities/errors/common";
import {
signInInputSchema,
type ISignInUseCase,
type SignInOutput,
type SignInRequestContext,
} from "../../application/use-cases/sign-in.use-case";
function presenter(value: SignInOutput) {
return value.cookie;
}
export type ISignInController = ReturnType<typeof signInController>;
export const signInController =
(signInUseCase: ISignInUseCase) =>
async (
input: unknown,
// Server-derived, never part of the client-facing input schema (B2):
// the tRPC adapter builds it from trusted proxy headers.
requestContext?: SignInRequestContext,
): Promise<ReturnType<typeof presenter>> => {
const parsed = signInInputSchema.safeParse(input);
if (!parsed.success) {
throw new InputParseError("Invalid sign-in input", {
cause: parsed.error,
});
}
const result = await signInUseCase({
...parsed.data,
clientIp: requestContext?.clientIp,
});
return presenter(result);
};