Browse Source

feat(auth): require captcha on login and register

Made-with: Cursor
main
npmrun 13 hours ago
parent
commit
b4a46cfcdc
  1. 28
      server/api/auth/login.post.ts
  2. 27
      server/api/auth/register.post.ts

28
server/api/auth/login.post.ts

@ -1,18 +1,30 @@
import { getRequestIP } from "h3";
import { loginUser } from "#server/service/auth"; import { loginUser } from "#server/service/auth";
import { toPublicAuthError } from "#server/service/auth/errors"; import { toPublicAuthError } from "#server/service/auth/errors";
import { setSessionCookie } from "#server/service/auth/cookie"; import { setSessionCookie } from "#server/service/auth/cookie";
import { captchaConsume } from "#server/service/captcha/store";
type LoginBody = { import { assertLoginRegisterCaptchaFieldsPresent } from "#server/service/captcha/validate-body";
username: string; import { assertUnderRateLimit } from "#server/utils/simple-rate-limit";
password: string;
};
export default defineWrappedResponseHandler(async (event) => { export default defineWrappedResponseHandler(async (event) => {
const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown";
assertUnderRateLimit(`auth-login:${ip}`, 30, 60_000);
const body = await readBody(event);
assertLoginRegisterCaptchaFieldsPresent(body);
if (!captchaConsume(body.captchaId, body.captchaAnswer)) {
throw createError({
statusCode: 400,
statusMessage: "验证码错误或已过期,请重试",
});
}
try { try {
const body = await readBody<LoginBody>(event); const result = await loginUser({
const result = await loginUser(body); username: body.username,
password: body.password,
});
setSessionCookie(event, result.sessionId); setSessionCookie(event, result.sessionId);
return R.success({ return R.success({
user: result.user, user: result.user,
}); });

27
server/api/auth/register.post.ts

@ -1,12 +1,23 @@
import { getRequestIP } from "h3";
import { registerUser } from "#server/service/auth"; import { registerUser } from "#server/service/auth";
import { toPublicAuthError } from "#server/service/auth/errors"; import { toPublicAuthError } from "#server/service/auth/errors";
import { captchaConsume } from "#server/service/captcha/store";
type RegisterBody = { import { assertLoginRegisterCaptchaFieldsPresent } from "#server/service/captcha/validate-body";
username: string; import { assertUnderRateLimit } from "#server/utils/simple-rate-limit";
password: string;
};
export default defineWrappedResponseHandler(async (event) => { export default defineWrappedResponseHandler(async (event) => {
const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown";
assertUnderRateLimit(`auth-register:${ip}`, 20, 60_000);
const body = await readBody(event);
assertLoginRegisterCaptchaFieldsPresent(body);
if (!captchaConsume(body.captchaId, body.captchaAnswer)) {
throw createError({
statusCode: 400,
statusMessage: "验证码错误或已过期,请重试",
});
}
const allowRegister = await event.context.config.getGlobal("allowRegister"); const allowRegister = await event.context.config.getGlobal("allowRegister");
if (!allowRegister) { if (!allowRegister) {
throw createError({ throw createError({
@ -16,8 +27,10 @@ export default defineWrappedResponseHandler(async (event) => {
} }
try { try {
const body = await readBody<RegisterBody>(event); const user = await registerUser({
const user = await registerUser(body); username: body.username,
password: body.password,
});
return R.success({ return R.success({
user, user,
}); });

Loading…
Cancel
Save