Browse Source

feat(auth): validate captcha fields on login/register body

Made-with: Cursor
main
npmrun 13 hours ago
parent
commit
011cf34fa8
  1. 12
      server/api/auth/captcha.get.ts
  2. 24
      server/service/captcha/validate-body.ts
  3. 1
      server/utils/auth-api-routes.ts

12
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 });
});

24
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<string, unknown>;
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: "请完成验证码" });
}
}

1
server/utils/auth-api-routes.ts

@ -4,6 +4,7 @@ type RouteRule = {
}; };
const API_ALLOWLIST: RouteRule[] = [ const API_ALLOWLIST: RouteRule[] = [
{ path: "/api/auth/captcha", methods: ["GET"] },
{ path: "/api/auth/login", methods: ["POST"] }, { path: "/api/auth/login", methods: ["POST"] },
{ path: "/api/auth/register", methods: ["POST"] }, { path: "/api/auth/register", methods: ["POST"] },
{ path: "/api/config/global", methods: ["GET"] }, { path: "/api/config/global", methods: ["GET"] },

Loading…
Cancel
Save