Skip to content

Commit

Permalink
Implement recaptcha (#9)
Browse files Browse the repository at this point in the history
* add logic to check reCaptcha value

* add custom exception

---------

Co-authored-by: NHT <[email protected]>
  • Loading branch information
nfesta2023 and hoangtuan910 authored Feb 23, 2024
1 parent 214f277 commit 8f56ee7
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 25 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## GOOGLE RECAPTCHA
GG_RECAPTCHA_SECRET_KEY=
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ lerna-debug.log*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/extensions.json
.env
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/axios": "^3.0.2",
"@nestjs/cache-manager": "^2.1.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.1.1",
"@nestjs/config": "^3.2.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/microservices": "^10.2.8",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^10.0.1",
"axios": "^1.6.1",
"axios": "^1.6.7",
"bcrypt": "^5.1.1",
"cache-manager": "^5.3.1",
"cache-manager-redis-store": "^3.0.1",
Expand Down
5 changes: 5 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Customer } from './entity/customer.entity';
import { ConfigModule } from '@nestjs/config';
// import { redisStore } from 'cache-manager-redis-yet';
import { CacheModule } from '@nestjs/cache-manager';
import { HttpModule } from '@nestjs/axios';

@Module({
imports: [
Expand All @@ -36,6 +37,10 @@ import { CacheModule } from '@nestjs/cache-manager';
}),
}),
TypeOrmModule.forFeature([Customer]),
HttpModule,
ConfigModule.forRoot({
envFilePath: ['.env'],
}),
],
controllers: [AppController, TokenController, OtpController],
providers: [
Expand Down
29 changes: 28 additions & 1 deletion src/controller/token.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Controller } from '@nestjs/common';
import { Controller, HttpException } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { GeneralErrorResponse } from 'src/dto/general-error-response.dto';
import { GeneralServiceResponse } from 'src/dto/general-service-response.dto';
import { VerifyReCaptchaRequest } from 'src/dto/verify-recaptcha-request.dto';
import { VerifyReCaptchaResponse } from 'src/dto/verify-recaptcha-response.dto';
import { TokenService } from 'src/service/token.service';

@Controller()
Expand All @@ -15,4 +19,27 @@ export class TokenController {
public async refreshToken(user) {
return await this.tokenService.refreshToken(user);
}

@MessagePattern({ cmd: 'verify_recaptcha' })
public async verifyReCaptcha(
data: VerifyReCaptchaRequest,
): Promise<GeneralServiceResponse> {
const res = new GeneralServiceResponse();
const { verified_token } = data;
try {
const result: VerifyReCaptchaResponse =
await this.tokenService.verifyReCaptchaFromEndPoint(verified_token);
res.statusCode = 200;
res.data = result;
} catch (error) {
if (error instanceof HttpException) {
res.statusCode = error.getStatus();
res.data = error.getResponse();
} else {
res.statusCode = 500;
res.data = new GeneralErrorResponse(9, error.toString());
}
}
return res;
}
}
9 changes: 9 additions & 0 deletions src/dto/general-error-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class GeneralErrorResponse {
constructor(_error_code: number = 0, _detail: any = null) {
this.error_code = _error_code;
this.detail = _detail;
return this;
}
error_code: number;
detail: any;
}
9 changes: 9 additions & 0 deletions src/dto/general-service-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class GeneralServiceResponse {
constructor() {
this.statusCode = null;
this.data = null;
}

statusCode: number;
data: any;
}
3 changes: 3 additions & 0 deletions src/dto/verify-recaptcha-request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class VerifyReCaptchaRequest {
verified_token: string;
}
6 changes: 6 additions & 0 deletions src/dto/verify-recaptcha-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class VerifyReCaptchaResponse {
success: boolean; // true|false
challenge_ts: number; // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
apk_package_name: string; // the package name of the app where the reCAPTCHA was solved
// error-codes: string[]; // optional
}
8 changes: 8 additions & 0 deletions src/exceptions/custom-bad-request.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BadRequestException } from '@nestjs/common';
import { GeneralErrorResponse } from 'src/dto/general-error-response.dto';

export class CustomBadRequestException extends BadRequestException {
constructor(public readonly error_response: GeneralErrorResponse) {
super(error_response);
}
}
8 changes: 8 additions & 0 deletions src/exceptions/custom-not-found.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { NotFoundException } from '@nestjs/common';
import { GeneralErrorResponse } from 'src/dto/general-error-response.dto';

export class CustomNotFoundException extends NotFoundException {
constructor(public readonly error_response: GeneralErrorResponse) {
super(error_response);
}
}
19 changes: 19 additions & 0 deletions src/service/token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ import {
JWT_SECRET_ACCESS_TOKEN,
JWT_SECRET_REFRESH_TOKEN,
} from 'src/app.constants';
import { VerifyReCaptchaResponse } from 'src/dto/verify-recaptcha-response.dto';
import { HttpService } from '@nestjs/axios';
import { lastValueFrom } from 'rxjs';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class TokenService {
constructor(
private readonly jwtService: JwtService,
private readonly customerService: CustomerService,
private readonly httpService: HttpService,
private readonly configService: ConfigService,
) {}
public async createToken(user: GenericUser) {
const data: JwtPayload = {
Expand Down Expand Up @@ -104,4 +110,17 @@ export class TokenService {
return result;
}
}

async verifyReCaptchaFromEndPoint(
captcha_value: string,
): Promise<VerifyReCaptchaResponse> {
const ggReCaptchaSecret = this.configService.get<string>(
'GG_RECAPTCHA_SECRET_KEY',
);
const request = this.httpService.post(
`https://www.google.com/recaptcha/api/siteverify?secret=${ggReCaptchaSecret}&response=${captcha_value}`,
);
const result = await lastValueFrom(request);
return result.data;
}
}
47 changes: 26 additions & 21 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,11 @@
semver "^7.3.5"
tar "^6.1.11"

"@nestjs/axios@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@nestjs/axios/-/axios-3.0.2.tgz#0078c101a29fb46f5c566d68a4315fddabc083ed"
integrity sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ==

"@nestjs/cache-manager@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@nestjs/cache-manager/-/cache-manager-2.1.1.tgz#abc042c6ac83400c64fd293b48ddb2dec99f6096"
Expand Down Expand Up @@ -738,15 +743,15 @@
iterare "1.2.1"
tslib "2.6.2"

"@nestjs/config@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@nestjs/config/-/config-3.1.1.tgz#51e23ed84debd08afb86acf7b92bc4bf341797da"
integrity sha512-qu5QlNiJdqQtOsnB6lx4JCXPQ96jkKUsOGd+JXfXwqJqZcOSAq6heNFg0opW4pq4J/VZoNwoo87TNnx9wthnqQ==
"@nestjs/config@^3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@nestjs/config/-/config-3.2.0.tgz#4ca70f88b0636f86741ba192dd51dd2f01eaa7c1"
integrity sha512-BpYRn57shg7CH35KGT6h+hT7ZucB6Qn2B3NBNdvhD4ApU8huS5pX/Wc2e/aO5trIha606Bz2a9t9/vbiuTBTww==
dependencies:
dotenv "16.3.1"
dotenv "16.4.1"
dotenv-expand "10.0.0"
lodash "4.17.21"
uuid "9.0.0"
uuid "9.0.1"

"@nestjs/core@^10.0.0":
version "10.2.8"
Expand Down Expand Up @@ -1589,12 +1594,12 @@ asynckit@^0.4.0:
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

axios@^1.6.1:
version "1.6.1"
resolved "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz"
integrity sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==
axios@^1.6.7:
version "1.6.7"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7"
integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==
dependencies:
follow-redirects "^1.15.0"
follow-redirects "^1.15.4"
form-data "^4.0.0"
proxy-from-env "^1.1.0"

Expand Down Expand Up @@ -2351,7 +2356,12 @@ [email protected]:
resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz"
integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==

[email protected], dotenv@^16.0.3:
[email protected]:
version "16.4.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.1.tgz#1d9931f1d3e5d2959350d1250efab299561f7f11"
integrity sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==

dotenv@^16.0.3:
version "16.3.1"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz"
integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
Expand Down Expand Up @@ -2802,10 +2812,10 @@ flatted@^3.2.9:
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==

follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
follow-redirects@^1.15.4:
version "1.15.5"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==

foreground-child@^3.1.0:
version "3.1.1"
Expand Down Expand Up @@ -5594,11 +5604,6 @@ [email protected]:
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==

[email protected]:
version "9.0.0"
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==

[email protected], uuid@^9.0.0:
version "9.0.1"
resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz"
Expand Down

0 comments on commit 8f56ee7

Please sign in to comment.