Skip to content

Migrating from @nestlab/google-recaptcha ​

@nestlab/google-recaptcha and this package share their shape: a module with forRoot and forRootAsync, a decorator that guards routes and one that injects the result. Replace the reCAPTCHA widget in the frontend, then rename things on the server with the tables below.

ts
// Before
GoogleRecaptchaModule.forRoot({
  secretKey: process.env.GOOGLE_RECAPTCHA_SECRET_KEY,
  response: (req) => req.headers.recaptcha,
  skipIf: process.env.NODE_ENV !== 'production',
  actions: ['SignUp', 'SignIn'],
  score: 0.8,
});

// After
TurnstileModule.forRoot({
  secretKey: process.env.TURNSTILE_SECRET_KEY,
  skipIf: process.env.NODE_ENV !== 'production',
  expectedAction: ['SignUp', 'SignIn'],
});

Module options ​

@nestlab/google-recaptchanest-cloudflare-turnstileNotes
secretKeysecretKeyThe widget's secret key from the Cloudflare dashboard
responsetokenResponseOptional. By default the guard reads the cf-turnstile-response body field or header
actionsexpectedActionAlso takes a single string
scorenoneTurnstile has no score: Cloudflare accepts or rejects the token
skipIfskipIfThe function gets the ExecutionContext instead of the request
remoteIpremoteIp
debugdebugLeaves tokens and secret keys out of the log
loggernoneLogs go through Nest's Logger, so app.useLogger() applies
networkfetchTurnstile has a single siteverify URL
axiosConfigtimeout, fetchNo axios. For proxies, see Outbound proxies
globalisGlobalDefaults to true
enterprisenoneTurnstile has one API for every plan

Decorators and services ​

@nestlab/google-recaptchanest-cloudflare-turnstile
@Recaptcha()@TurnstileCaptcha()
@Recaptcha({ action })@TurnstileCaptcha({ action })
@Recaptcha({ response })tokenResponse on the module or on a widget
@RecaptchaResult()@TurnstileResult()
RecaptchaVerificationResultTurnstileVerifyResponse
GoogleRecaptchaValidator.validate()TurnstileService.validateToken()
GoogleRecaptchaExceptionBadRequestException, or your own from exceptionFactory
RecaptchaConfigRefnone. Load the options at startup with forRootAsync

validateToken() resolves with Cloudflare's answer as it is. Check result.success and result['error-codes'] instead of result.errors.

Error codes ​

@nestlab/google-recaptchanest-cloudflare-turnstile
MissingInputResponsereason missing
InvalidInputResponseinvalid-input-response
TimeoutOrDuplicatetimeout-or-duplicate
ForbiddenActionaction-mismatch
LowScorenone
MissingInputSecret, InvalidInputSecret, BadRequest, NetworkErrora 500, and a TurnstileError for onError

exceptionFactory gets the codes in details.errorCodes.

Frontend ​

reCAPTCHA setups often send the token in a recaptcha header. Send the Turnstile token in cf-turnstile-response and leave tokenResponse out, or keep your header and point tokenResponse at it. Frontend frameworks has examples for React, Vue and Angular.