diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/@types/recaptcha-promise.d.ts | 16 | ||||
| -rw-r--r-- | src/misc/captcha.ts | 56 | ||||
| -rw-r--r-- | src/server/api/private/signup.ts | 24 |
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'); - } } } |