'Nestjs and Google Recaptcha

I am trying to implement a server-side validation for reCaptcha using Nestjs and I want to if I should implement this as a module or as a service for modules(such as a self written user authentication module) that would require the use of reCaptcha.



Solution 1:[1]

I solved it by creating a seperate RecaptchaGuard.

// recaptcha.guard.ts
import {
  Injectable,
  CanActivate,
  ExecutionContext,
  HttpService,
  ForbiddenException,
} from "@nestjs/common";

@Injectable()
export class RecaptchaGuard implements CanActivate {
  constructor(private readonly httpService: HttpService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const { body } = context.switchToHttp().getRequest();

    const { data } = await this.httpService
      .post(
        `https://www.google.com/recaptcha/api/siteverify?response=${body.recaptchaValue}&secret=${process.env.RECAPTCHA_SECRET}`
      )
      .toPromise();

    if (!data.success) {
      throw new ForbiddenException();
    }

    return true;
  }
}

Next you can simply apply the recaptcha guard on a controller.

// app.controller.ts
import { Controller, Post, UseGuard } from '@nestjs/common';
import { RecaptchaGuard } from './recaptcha.guard.ts'
    
@Controller()
export class AppController {

 @Post()
 @UseGuard(RecaptchaGuard)
 async postForm(){
  //
 }
}

Solution 2:[2]

Import HttpModule

import { Module } from '@nestjs/common';
import { HttpModule } from "@nestjs/axios";

@Module({
    imports: [HttpModule],
    ...
})

Then create a service to validate captcha value

NOTE: You have to get the secret/site key from here (site key will get used in the client)

import { HttpService, Inject, Injectable } from "@nestjs/common";
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
import { map } from 'rxjs/operators'

@Injectable()
export class CaptchaService {
    constructor(
        @Inject(REQUEST) private readonly request: Request,
        private httpService: HttpService) { }

    public validate(value:string): Promise<any> {
        const remoteAddress = this.request.socket.remoteAddress
        const secretKey = "XXXXXXXXX"
        const url = "https://www.google.com/recaptcha/api/siteverify?secret=" + secretKey + "&response=" + value + "&remoteip=" + remoteAddress;

        return this.httpService.post(url).pipe(map(response => {
            return response['data']
        })).toPromise()
    }
    
}

then in your controller :

 const value="XXXXX" // client send this for you
 const result = await this.captchService.validate(value)
 if (!result.success) throw new BadRequestException()

Client Side

If you are using angular you can use this

Solution 3:[3]

Initially, I was following the accepted answer, and then noticed that there's an easier way to do so - and wanna share it just in case it can help anyone.

There's a NestJS module that provides easy integration with ReCAPTCHA: https://github.com/chvarkov/google-recaptcha.

  • Install the module.
  • In your AppModule create something like this:
    const googleRecaptchaFactory = (
      applicationConfigService: ApplicationConfigService,
    ) => ({
      secretKey: applicationConfigService.auth.recaptcha.secretKey,
      response: (req) => req.headers.recaptcha || '',
      skipIf: applicationConfigService.auth.recaptcha.bypassVerification,
    });
    
    @Module({
      controllers: [/* ... */],
      imports: [
        /* ... */
        GoogleRecaptchaModule.forRootAsync({
          imports: [],
          inject: [ApplicationConfigService],
          useFactory: googleRecaptchaFactory,
        }),
      ],
      providers: [/* ... */],
      exports: [/* ... */],
    })
    export class Module {}

  • Now, in your controller, use the @Recapcha guard.

You can find documentation for the entire integration process in the linked Github.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 OzB