'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 AppModulecreate 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 @Recapchaguard.
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 | 
