summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/@types/recaptcha-promise.d.ts16
-rw-r--r--src/misc/captcha.ts56
-rw-r--r--src/server/api/private/signup.ts24
3 files changed, 62 insertions, 34 deletions
diff --git a/src/@types/recaptcha-promise.d.ts b/src/@types/recaptcha-promise.d.ts
deleted file mode 100644
index cfbd5eebf2..0000000000
--- a/src/@types/recaptcha-promise.d.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-declare module 'recaptcha-promise' {
- interface IVerifyOptions {
- secret_key?: string;
- }
-
- interface IVerify {
- (response: string, remoteAddress?: string): Promise<boolean>;
- init(options: IVerifyOptions): IVerify;
- }
-
- namespace recaptchaPromise {} // Hack
-
- const verify: IVerify;
-
- export = verify;
-}
diff --git a/src/misc/captcha.ts b/src/misc/captcha.ts
new file mode 100644
index 0000000000..87ec143ca8
--- /dev/null
+++ b/src/misc/captcha.ts
@@ -0,0 +1,56 @@
+import fetch from 'node-fetch';
+import { URLSearchParams } from 'url';
+import { getAgentByUrl } from './fetch';
+import config from '../config';
+
+export async function verifyRecaptcha(secret: string, response: string) {
+ const result = await getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(e => {
+ throw `recaptcha-request-failed: ${e}`;
+ });
+
+ if (result.success !== true) {
+ const errorCodes = result['error-codes'] ? result['error-codes']?.join(', ') : '';
+ throw `recaptcha-failed: ${errorCodes}`;
+ }
+}
+
+export async function verifyHcaptcha(secret: string, response: string) {
+ const result = await getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(e => {
+ throw `hcaptcha-request-failed: ${e}`;
+ });
+
+ if (result.success !== true) {
+ const errorCodes = result['error-codes'] ? result['error-codes']?.join(', ') : '';
+ throw `hcaptcha-failed: ${errorCodes}`;
+ }
+}
+
+type CaptchaResponse = {
+ success: boolean;
+ 'error-codes'?: string[];
+};
+
+async function getCaptchaResponse(url: string, secret: string, response: string): Promise<CaptchaResponse> {
+ const params = new URLSearchParams({
+ secret,
+ response
+ });
+
+ const res = await fetch(url, {
+ method: 'POST',
+ body: params,
+ headers: {
+ 'User-Agent': config.userAgent
+ },
+ timeout: 10 * 1000,
+ agent: getAgentByUrl
+ }).catch(e => {
+ throw `${e.message || e}`;
+ });
+
+ if (!res.ok) {
+ throw `${res.status}`;
+ }
+
+ return await res.json() as CaptchaResponse;
+}
diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts
index 6dc252ac45..3d467a0e68 100644
--- a/src/server/api/private/signup.ts
+++ b/src/server/api/private/signup.ts
@@ -1,7 +1,6 @@
import * as Koa from 'koa';
import { fetchMeta } from '../../../misc/fetch-meta';
-import { verify } from 'hcaptcha';
-import * as recaptcha from 'recaptcha-promise';
+import { verifyHcaptcha, verifyRecaptcha } from '../../../misc/captcha';
import { Users, RegistrationTickets } from '../../../models';
import { signup } from '../common/signup';
@@ -14,26 +13,15 @@ export default async (ctx: Koa.Context) => {
// ただしテスト時はこの機構は障害となるため無効にする
if (process.env.NODE_ENV !== 'test') {
if (instance.enableHcaptcha && instance.hcaptchaSecretKey) {
- const success = await verify(instance.hcaptchaSecretKey, body['hcaptcha-response']).then(
- ({ success }) => success,
- () => false,
- );
-
- if (!success) {
- ctx.throw(400, 'hcaptcha-failed');
- }
+ await verifyHcaptcha(instance.hcaptchaSecretKey, body['hcaptcha-response']).catch(e => {
+ ctx.throw(400, e);
+ });
}
if (instance.enableRecaptcha && instance.recaptchaSecretKey) {
- recaptcha.init({
- secret_key: instance.recaptchaSecretKey
+ await verifyRecaptcha(instance.recaptchaSecretKey, body['g-recaptcha-response']).catch(e => {
+ ctx.throw(400, e);
});
-
- const success = await recaptcha(body['g-recaptcha-response']);
-
- if (!success) {
- ctx.throw(400, 'recaptcha-failed');
- }
}
}