fix(#410): use toNodeHandler for BetterAuth Express compatibility
Some checks failed
ci/woodpecker/push/api Pipeline failed

BetterAuth expects Web API Request objects (Fetch API standard) with
headers.get(), but NestJS/Express passes IncomingMessage objects with
headers[] property access. Use better-auth/node's toNodeHandler to
properly convert between Express req/res and BetterAuth's Web API handler.

Also fixes vitest SWC config to read the correct tsconfig for NestJS
decorator metadata emission, which was causing DI injection failures
in tests.

Fixes #410

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 19:03:21 -06:00
parent ca21416efc
commit ba54de88fd
5 changed files with 51 additions and 31 deletions

View File

@@ -1,5 +1,6 @@
import { Controller, All, Req, Get, UseGuards, Request, Logger } from "@nestjs/common";
import { Controller, All, Req, Res, Get, UseGuards, Request, Logger } from "@nestjs/common";
import { Throttle } from "@nestjs/throttler";
import type { Request as ExpressRequest, Response as ExpressResponse } from "express";
import type { AuthUser, AuthSession } from "@mosaic/shared";
import { AuthService } from "./auth.service";
import { AuthGuard } from "./guards/auth.guard";
@@ -88,37 +89,29 @@ export class AuthController {
*/
@All("*")
@Throttle({ strict: { limit: 10, ttl: 60000 } })
async handleAuth(@Req() req: Request): Promise<unknown> {
async handleAuth(@Req() req: ExpressRequest, @Res() res: ExpressResponse): Promise<void> {
// Extract client IP for logging
const clientIp = this.getClientIp(req);
const requestPath = (req as unknown as { url?: string }).url ?? "unknown";
const method = (req as unknown as { method?: string }).method ?? "UNKNOWN";
// Log auth catch-all hits for monitoring and debugging
this.logger.debug(`Auth catch-all: ${method} ${requestPath} from ${clientIp}`);
this.logger.debug(`Auth catch-all: ${req.method} ${req.url} from ${clientIp}`);
const auth = this.authService.getAuth();
return auth.handler(req);
const handler = this.authService.getNodeHandler();
return handler(req, res);
}
/**
* Extract client IP from request, handling proxies
*/
private getClientIp(req: Request): string {
const reqWithHeaders = req as unknown as {
headers?: Record<string, string | string[] | undefined>;
ip?: string;
socket?: { remoteAddress?: string };
};
private getClientIp(req: ExpressRequest): string {
// Check X-Forwarded-For header (for reverse proxy setups)
const forwardedFor = reqWithHeaders.headers?.["x-forwarded-for"];
const forwardedFor = req.headers["x-forwarded-for"];
if (forwardedFor) {
const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
return ips?.split(",")[0]?.trim() ?? "unknown";
}
// Fall back to direct IP
return reqWithHeaders.ip ?? reqWithHeaders.socket?.remoteAddress ?? "unknown";
return req.ip ?? req.socket.remoteAddress ?? "unknown";
}
}