Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,11 @@ export class AuthConfigService {
86400,
);
}

/**
* Human APP email.
*/
get humanAppEmail(): string {
return this.configService.getOrThrow<string>('HUMAN_APP_EMAIL');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,7 @@ export const envValidator = Joi.object({
KYC_API_KEY: Joi.string(),
KYC_API_PRIVATE_KEY: Joi.string().required(),
KYC_BASE_URL: Joi.string(),

// Human App
HUMAN_APP_EMAIL: Joi.string().email().required(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ export enum ErrorUser {
DuplicatedAddress = 'The address you are trying to use already exists. Please check that the address is correct or use a different address.',
}

/**
* Represents error messages related to captcha.
*/
export enum ErrorCapthca {
InvalidToken = 'Invalid captcha token provided',
VerificationFailed = 'Captcha verification failed',
}

/**
* Represents error messages related to send grid.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
Injectable,
CanActivate,
ExecutionContext,
HttpStatus,
HttpException,
Logger,
} from '@nestjs/common';
import { Request } from 'express';
import { HCaptchaService } from '../../integrations/hcaptcha/hcaptcha.service';
import { AuthConfigService } from '../config/auth-config.service';

@Injectable()
export class HCaptchaGuard implements CanActivate {
logger = new Logger(HCaptchaGuard.name);
constructor(
private readonly hCaptchaService: HCaptchaService,
private readonly authConfigSerice: AuthConfigService,
) {}
public async canActivate(context: ExecutionContext): Promise<boolean> {
const request: Request = context.switchToHttp().getRequest();

const { body } = request;
const hCaptchaToken = body['h_captcha_token'];

// TODO: Remove 27-45 lines once we figure out how to replace human app user
if (request.path === '/auth/signin') {
const email = body['email'];
// Checking email here to avoid unnecessary db calls
if (email === this.authConfigSerice.humanAppEmail) {
return true;
}
}

if (!hCaptchaToken) {
const message = 'hCaptcha token not provided';
this.logger.error(message, request.path);
throw new HttpException(
{
message,
timestamp: new Date().toISOString(),
},
HttpStatus.BAD_REQUEST,
);
}

const captchaVerificationResult = await this.hCaptchaService.verifyToken({
token: hCaptchaToken,
});
if (!captchaVerificationResult.success) {
throw new HttpException('Invalid hCaptcha token', HttpStatus.BAD_REQUEST);
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from './auth.dto';
import { AuthService } from './auth.service';
import { JwtAuthGuard } from '../../common/guards';
import { HCaptchaGuard } from '../../common/guards/hcaptcha';
import { RequestWithUser } from '../../common/types';
import { TokenRepository } from './token.repository';
import { TokenType } from './token.entity';
Expand Down Expand Up @@ -66,6 +67,7 @@ export class AuthJwtController {

@Public()
@Post('/signup')
@UseGuards(HCaptchaGuard)
@UseInterceptors(ClassSerializerInterceptor)
@ApiOperation({
summary: 'User Signup',
Expand All @@ -87,6 +89,7 @@ export class AuthJwtController {

@Public()
@Post('/signin')
@UseGuards(HCaptchaGuard)
@HttpCode(200)
@ApiOperation({
summary: 'User Signin',
Expand Down Expand Up @@ -188,6 +191,7 @@ export class AuthJwtController {

@Public()
@Post('/forgot-password')
@UseGuards(HCaptchaGuard)
@HttpCode(204)
@ApiOperation({
summary: 'Forgot Password',
Expand All @@ -212,6 +216,7 @@ export class AuthJwtController {

@Public()
@Post('/restore-password')
@UseGuards(HCaptchaGuard)
@HttpCode(204)
@ApiOperation({
summary: 'Restore Password',
Expand Down Expand Up @@ -251,7 +256,7 @@ export class AuthJwtController {
}

@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@UseGuards(HCaptchaGuard, JwtAuthGuard)
@HttpCode(204)
@Post('/resend-email-verification')
@ApiOperation({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ErrorCapthca } from '../../common/constants/errors';
import { BaseError } from '../../common/errors/base';

export enum AuthErrorMessage {
Expand All @@ -9,7 +8,7 @@ export enum AuthErrorMessage {
}

export class AuthError extends BaseError {
constructor(message: AuthErrorMessage | ErrorCapthca) {
constructor(message: AuthErrorMessage) {
super(message);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

import { ErrorCapthca } from '../../common/constants/errors';
import {
OperatorStatus,
Role as UserRole,
Expand Down Expand Up @@ -65,14 +64,9 @@ export class AuthService {
private readonly sendgridService: SendGridService,
private readonly web3Service: Web3Service,
private readonly userRepository: UserRepository,
private readonly hCaptchaService: HCaptchaService,
) {}

public async signin({
email,
password,
hCaptchaToken,
}: SignInDto): Promise<AuthDto> {
public async signin({ email, password }: SignInDto): Promise<AuthDto> {
const userEntity = await this.userRepository.findOneByEmail(email);
if (!userEntity) {
throw new AuthError(AuthErrorMessage.INVALID_CREDENTIALS);
Expand All @@ -82,33 +76,10 @@ export class AuthService {
throw new AuthError(AuthErrorMessage.INVALID_CREDENTIALS);
}

if (userEntity.role !== UserRole.HUMAN_APP) {
if (!hCaptchaToken) {
throw new AuthError(ErrorCapthca.InvalidToken);
}

const captchaVerificationResult = await this.hCaptchaService.verifyToken({
token: hCaptchaToken,
});
if (!captchaVerificationResult.success) {
throw new AuthError(ErrorCapthca.VerificationFailed);
}
}

return this.auth(userEntity);
}

public async signup(data: UserCreateDto): Promise<UserEntity> {
if (!data.hCaptchaToken) {
throw new AuthError(ErrorCapthca.InvalidToken);
}
const captchaVerificationResult = await this.hCaptchaService.verifyToken({
token: data.hCaptchaToken,
});
if (!captchaVerificationResult.success) {
throw new AuthError(ErrorCapthca.VerificationFailed);
}

const storedUser = await this.userRepository.findOneByEmail(data.email);
if (storedUser) {
throw new DuplicatedUserError(data.email);
Expand Down Expand Up @@ -237,16 +208,6 @@ export class AuthService {
}

public async forgotPassword(data: ForgotPasswordDto): Promise<void> {
if (!data.hCaptchaToken) {
throw new AuthError(ErrorCapthca.InvalidToken);
}
const captchaVerificationResult = await this.hCaptchaService.verifyToken({
token: data.hCaptchaToken,
});
if (!captchaVerificationResult.success) {
throw new AuthError(ErrorCapthca.VerificationFailed);
}

const userEntity = await this.userRepository.findOneByEmail(data.email);

if (!userEntity) {
Expand Down Expand Up @@ -287,16 +248,6 @@ export class AuthService {
}

public async restorePassword(data: RestorePasswordDto): Promise<void> {
if (!data.hCaptchaToken) {
throw new AuthError(ErrorCapthca.InvalidToken);
}
const captchaVerificationResult = await this.hCaptchaService.verifyToken({
token: data.hCaptchaToken,
});
if (!captchaVerificationResult.success) {
throw new AuthError(ErrorCapthca.VerificationFailed);
}

const tokenEntity = await this.tokenRepository.findOneByUuidAndType(
data.token,
TokenType.PASSWORD,
Expand Down Expand Up @@ -347,16 +298,6 @@ export class AuthService {
public async resendEmailVerification(
data: ResendEmailVerificationDto,
): Promise<void> {
if (!data.hCaptchaToken) {
throw new AuthError(ErrorCapthca.InvalidToken);
}
const captchaVerificationResult = await this.hCaptchaService.verifyToken({
token: data.hCaptchaToken,
});
if (!captchaVerificationResult.success) {
throw new AuthError(ErrorCapthca.VerificationFailed);
}

const userEntity = await this.userRepository.findOneByEmail(data.email);
if (!userEntity || userEntity.status !== UserStatus.PENDING) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
RegistrationInExchangeOracleResponseDto,
} from './user.dto';
import { JwtAuthGuard } from '../../common/guards';
import { HCaptchaGuard } from '../../common/guards/hcaptcha';
import { RequestWithUser } from '../../common/types';
import { UserService } from './user.service';
import { Public } from '../../common/decorators';
Expand All @@ -35,12 +36,12 @@ import { KycSignedAddressDto } from '../kyc/kyc.dto';
@ApiTags('User')
@Controller('/user')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
export class UserController {
constructor(private readonly userService: UserService) {}

@Post('/register-labeler')
@HttpCode(200)
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: 'Register Labeler',
description: 'Endpoint to register user as a labeler on hcaptcha services.',
Expand Down Expand Up @@ -72,6 +73,7 @@ export class UserController {

@Post('/register-address')
@HttpCode(200)
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: 'Register Blockchain Address',
description: 'Endpoint to register blockchain address.',
Expand Down Expand Up @@ -103,6 +105,7 @@ export class UserController {

@Post('/enable-operator')
@HttpCode(204)
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: 'Enable an operator',
description: 'Endpoint to enable an operator.',
Expand All @@ -125,6 +128,7 @@ export class UserController {

@Post('/disable-operator')
@HttpCode(204)
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: 'Disable an operator',
description: 'Endpoint to disable an operator.',
Expand All @@ -147,6 +151,7 @@ export class UserController {

@Public()
@Post('/prepare-signature')
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: 'Web3 signature body',
description:
Expand All @@ -170,6 +175,7 @@ export class UserController {

@Post('/exchange-oracle-registration')
@HttpCode(200)
@UseGuards(HCaptchaGuard, JwtAuthGuard)
@ApiOperation({
summary: 'Notifies registration in Exchange Oracle completed',
description:
Expand All @@ -193,13 +199,17 @@ export class UserController {
@Req() request: RequestWithUser,
@Body() data: RegistrationInExchangeOracleDto,
): Promise<RegistrationInExchangeOracleResponseDto> {
await this.userService.registrationInExchangeOracle(request.user, data);
await this.userService.registrationInExchangeOracle(
request.user,
data.oracleAddress,
);

return { oracleAddress: data.oracleAddress };
}

@Get('/exchange-oracle-registration')
@HttpCode(200)
@UseGuards(JwtAuthGuard)
@ApiOperation({
summary: 'Retrieves Exchange Oracles the user is registered in',
description:
Expand Down
Loading
Loading