mirror of
https://github.com/movie-web/backend.git
synced 2025-09-13 13:03:26 +00:00
add captcha support
This commit is contained in:
@@ -26,7 +26,7 @@ Backend for movie-web
|
|||||||
- [ ] provider metrics
|
- [ ] provider metrics
|
||||||
- [ ] ratelimits (stored in redis)
|
- [ ] ratelimits (stored in redis)
|
||||||
- [X] switch to pnpm
|
- [X] switch to pnpm
|
||||||
- [ ] catpcha support
|
- [X] catpcha support
|
||||||
- [ ] global namespacing (accounts are stored on a namespace)
|
- [ ] global namespacing (accounts are stored on a namespace)
|
||||||
- [ ] cleanup jobs
|
- [ ] cleanup jobs
|
||||||
- [ ] cleanup expired sessions
|
- [ ] cleanup expired sessions
|
||||||
|
@@ -48,4 +48,13 @@ export const configSchema = z.object({
|
|||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string().min(1).optional(),
|
description: z.string().min(1).optional(),
|
||||||
}),
|
}),
|
||||||
|
captcha: z
|
||||||
|
.object({
|
||||||
|
// enabled captchas on register
|
||||||
|
enabled: z.coerce.boolean().default(false),
|
||||||
|
|
||||||
|
// captcha secret
|
||||||
|
secret: z.string().min(1).optional(),
|
||||||
|
})
|
||||||
|
.default({}),
|
||||||
});
|
});
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { formatSession } from '@/db/models/Session';
|
import { formatSession } from '@/db/models/Session';
|
||||||
import { User, formatUser } from '@/db/models/User';
|
import { User, formatUser } from '@/db/models/User';
|
||||||
|
import { assertCaptcha } from '@/services/captcha';
|
||||||
import { handle } from '@/services/handler';
|
import { handle } from '@/services/handler';
|
||||||
import { makeRouter } from '@/services/router';
|
import { makeRouter } from '@/services/router';
|
||||||
import { makeSession, makeSessionToken } from '@/services/session';
|
import { makeSession, makeSessionToken } from '@/services/session';
|
||||||
@@ -13,6 +14,7 @@ const registerSchema = z.object({
|
|||||||
colorB: z.string(),
|
colorB: z.string(),
|
||||||
icon: z.string(),
|
icon: z.string(),
|
||||||
}),
|
}),
|
||||||
|
captchaToken: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const manageAuthRouter = makeRouter((app) => {
|
export const manageAuthRouter = makeRouter((app) => {
|
||||||
@@ -20,6 +22,8 @@ export const manageAuthRouter = makeRouter((app) => {
|
|||||||
'/auth/register',
|
'/auth/register',
|
||||||
{ schema: { body: registerSchema } },
|
{ schema: { body: registerSchema } },
|
||||||
handle(async ({ em, body, req }) => {
|
handle(async ({ em, body, req }) => {
|
||||||
|
await assertCaptcha(body.captchaToken);
|
||||||
|
|
||||||
const user = new User();
|
const user = new User();
|
||||||
user.name = body.name;
|
user.name = body.name;
|
||||||
user.profile = body.profile;
|
user.profile = body.profile;
|
||||||
|
@@ -22,6 +22,7 @@ export const metaRouter = makeRouter((app) => {
|
|||||||
return {
|
return {
|
||||||
name: conf.meta.name,
|
name: conf.meta.name,
|
||||||
description: conf.meta.description,
|
description: conf.meta.description,
|
||||||
|
hasCaptcha: conf.captcha.enabled,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
28
src/services/captcha.ts
Normal file
28
src/services/captcha.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { conf } from '@/config';
|
||||||
|
import { StatusError } from '@/services/error';
|
||||||
|
|
||||||
|
export async function isValidCaptcha(token: string): Promise<boolean> {
|
||||||
|
if (!conf.captcha.secret)
|
||||||
|
throw new Error('isValidCaptcha() is called but no secret set');
|
||||||
|
const res = await fetch('https://www.google.com/recaptcha/api/siteverify', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
secret: conf.captcha.secret,
|
||||||
|
response: token,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const json = await res.json();
|
||||||
|
return !!json.success;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function assertCaptcha(token?: string) {
|
||||||
|
// early return if captchas arent enabled
|
||||||
|
if (!conf.captcha.enabled) return;
|
||||||
|
if (!token) throw new StatusError('captcha token is required', 400);
|
||||||
|
|
||||||
|
const isValid = await isValidCaptcha(token);
|
||||||
|
if (!isValid) throw new StatusError('captcha token is invalid', 400);
|
||||||
|
}
|
Reference in New Issue
Block a user