Refactor CAPTCHA setup verification

Eliminated constant verification of CAPTCHA setup across different modules and refactored the CAPTCHA token verification process. This commit relies on config parameters to decide whether to verify CAPTCHA or not, instead of environment variables. It also reforms the token submission to CAPTCHA service for consistency and clarity.
This commit is contained in:
giancarlo
2024-04-27 19:13:11 +07:00
parent 0616d3b288
commit 84cacb81c2
3 changed files with 35 additions and 37 deletions

View File

@@ -3,20 +3,26 @@ import 'server-only';
const verifyEndpoint = const verifyEndpoint =
'https://challenges.cloudflare.com/turnstile/v0/siteverify'; 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
const secret = process.env.CAPTCHA_SECRET_TOKEN; const CAPTCHA_SECRET_TOKEN = process.env.CAPTCHA_SECRET_TOKEN;
/** /**
* Verify the CAPTCHA token with the CAPTCHA service * @name verifyCaptchaToken
* @param token * @description Verify the CAPTCHA token with the CAPTCHA service
* @param token - The CAPTCHA token to verify
*/ */
export async function verifyCaptchaToken(token: string) { export async function verifyCaptchaToken(token: string) {
if (!secret) { if (!CAPTCHA_SECRET_TOKEN) {
throw new Error('CAPTCHA_SECRET_TOKEN is not set'); throw new Error('CAPTCHA_SECRET_TOKEN is not set');
} }
const formData = new FormData();
formData.append('secret', CAPTCHA_SECRET_TOKEN);
formData.append('response', token);
const res = await fetch(verifyEndpoint, { const res = await fetch(verifyEndpoint, {
method: 'POST', method: 'POST',
body: `secret=${encodeURIComponent(secret)}&response=${encodeURIComponent(token)}`, body: formData,
headers: { headers: {
'content-type': 'application/x-www-form-urlencoded', 'content-type': 'application/x-www-form-urlencoded',
}, },

View File

@@ -13,13 +13,6 @@ import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-clie
import { captureException, zodParseFactory } from '../utils'; import { captureException, zodParseFactory } from '../utils';
/** /**
* @name IS_CAPTCHA_SETUP
* @description Check if the CAPTCHA is setup
*/
const IS_CAPTCHA_SETUP = !!process.env.CAPTCHA_SECRET_TOKEN;
/**
*
* @name enhanceAction * @name enhanceAction
* @description Enhance an action with captcha, schema and auth checks * @description Enhance an action with captcha, schema and auth checks
*/ */
@@ -48,9 +41,25 @@ export function enhanceAction<
type UserParam = Config['auth'] extends false ? undefined : User; type UserParam = Config['auth'] extends false ? undefined : User;
const requireAuth = config.auth ?? true; const requireAuth = config.auth ?? true;
let user: UserParam = undefined as UserParam; let user: UserParam = undefined as UserParam;
// validate the schema passed in the config if it exists
const data = config.schema
? zodParseFactory(config.schema)(params)
: params;
// by default, the CAPTCHA token is not required
const verifyCaptcha = config.captcha ?? false;
// verify the CAPTCHA token. It will throw an error if the token is invalid.
if (verifyCaptcha) {
const token = (data as Args & { captchaToken: string }).captchaToken;
// Verify the CAPTCHA token. It will throw an error if the token is invalid.
await verifyCaptchaToken(token);
}
// verify the user is authenticated if required
if (requireAuth) { if (requireAuth) {
// verify the user is authenticated if required // verify the user is authenticated if required
const auth = await requireUser(getSupabaseServerActionClient()); const auth = await requireUser(getSupabaseServerActionClient());
@@ -63,35 +72,23 @@ export function enhanceAction<
user = auth.data as UserParam; user = auth.data as UserParam;
} }
// validate the schema
const data = config.schema
? zodParseFactory(config.schema)(params)
: params;
// verify captcha unless it's explicitly disabled
// verify the captcha token if required
const verifyCaptcha = config.captcha ?? IS_CAPTCHA_SETUP;
if (verifyCaptcha) {
const token = (data as Args & { captchaToken: string }).captchaToken;
// Verify the CAPTCHA token. It will throw an error if the token is invalid.
await verifyCaptchaToken(token);
}
// capture exceptions if required // capture exceptions if required
const shouldCaptureException = config.captureException ?? true; const shouldCaptureException = config.captureException ?? true;
// if the action should capture exceptions, wrap the action in a try/catch block
if (shouldCaptureException) { if (shouldCaptureException) {
try { try {
// pass the data to the action
return await fn(data, user); return await fn(data, user);
} catch (error) { } catch (error) {
// capture the exception
await captureException(error); await captureException(error);
// re-throw the error
throw error; throw error;
} }
} else { } else {
// pass the data to the action // no need to capture exceptions, just pass the data to the action
return fn(data, user); return fn(data, user);
} }
}; };

View File

@@ -19,12 +19,6 @@ interface HandlerParams<Body> {
body: Body; body: Body;
} }
/**
* @name IS_CAPTCHA_SETUP
* @description Check if the CAPTCHA is setup
*/
const IS_CAPTCHA_SETUP = !!process.env.CAPTCHA_SECRET_TOKEN;
/** /**
* Enhanced route handler function. * Enhanced route handler function.
* *
@@ -69,7 +63,8 @@ export const enhanceRouteHandler = <
* This function takes a request object as an argument and returns a response object. * This function takes a request object as an argument and returns a response object.
*/ */
return async function routeHandler(request: NextRequest) { return async function routeHandler(request: NextRequest) {
const shouldVerifyCaptcha = params?.captcha ?? IS_CAPTCHA_SETUP; // Check if the captcha token should be verified
const shouldVerifyCaptcha = params?.captcha ?? false;
// Verify the captcha token if required and setup // Verify the captcha token if required and setup
if (shouldVerifyCaptcha) { if (shouldVerifyCaptcha) {