From 011cf34fa856eafae24f7f8eb6dff7e4eebf21f4 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Sun, 19 Apr 2026 00:46:59 +0800 Subject: [PATCH] feat(auth): validate captcha fields on login/register body Made-with: Cursor --- server/api/auth/captcha.get.ts | 12 ++++++++++++ server/service/captcha/validate-body.ts | 24 ++++++++++++++++++++++++ server/utils/auth-api-routes.ts | 1 + 3 files changed, 37 insertions(+) create mode 100644 server/api/auth/captcha.get.ts create mode 100644 server/service/captcha/validate-body.ts diff --git a/server/api/auth/captcha.get.ts b/server/api/auth/captcha.get.ts new file mode 100644 index 0000000..d319179 --- /dev/null +++ b/server/api/auth/captcha.get.ts @@ -0,0 +1,12 @@ +import { getRequestIP } from "h3"; +import { createCaptchaChallenge } from "#server/service/captcha/challenge"; +import { assertUnderRateLimit } from "#server/utils/simple-rate-limit"; + +export default defineWrappedResponseHandler(async (event) => { + setResponseHeader(event, "cache-control", "no-store"); + const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown"; + assertUnderRateLimit(`auth-captcha:${ip}`, 60, 60_000); + + const { captchaId, imageSvg } = createCaptchaChallenge(); + return R.success({ captchaId, imageSvg }); +}); diff --git a/server/service/captcha/validate-body.ts b/server/service/captcha/validate-body.ts new file mode 100644 index 0000000..88bbb79 --- /dev/null +++ b/server/service/captcha/validate-body.ts @@ -0,0 +1,24 @@ +export type AuthCredentialsAndCaptcha = { + username: string; + password: string; + captchaId: string; + captchaAnswer: string; +}; + +export function assertLoginRegisterCaptchaFieldsPresent( + body: unknown, +): asserts body is AuthCredentialsAndCaptcha { + if (typeof body !== "object" || body === null) { + throw createError({ statusCode: 400, statusMessage: "无效请求" }); + } + const b = body as Record; + if (typeof b.username !== "string" || typeof b.password !== "string") { + throw createError({ statusCode: 400, statusMessage: "无效请求" }); + } + if (typeof b.captchaId !== "string" || b.captchaId.trim() === "") { + throw createError({ statusCode: 400, statusMessage: "请完成验证码" }); + } + if (typeof b.captchaAnswer !== "string" || b.captchaAnswer.trim() === "") { + throw createError({ statusCode: 400, statusMessage: "请完成验证码" }); + } +} diff --git a/server/utils/auth-api-routes.ts b/server/utils/auth-api-routes.ts index 57c6b0a..ab942ed 100644 --- a/server/utils/auth-api-routes.ts +++ b/server/utils/auth-api-routes.ts @@ -4,6 +4,7 @@ type RouteRule = { }; const API_ALLOWLIST: RouteRule[] = [ + { path: "/api/auth/captcha", methods: ["GET"] }, { path: "/api/auth/login", methods: ["POST"] }, { path: "/api/auth/register", methods: ["POST"] }, { path: "/api/config/global", methods: ["GET"] },