Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
✨ Add 2FA enable/disable endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Oct 29, 2020
1 parent 9aaee67 commit cee3a55
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,11 @@ export class AuthService {
throw new UnauthorizedException(
'Two-factor authentication code is invalid',
);
return this.prisma.users.update({
const result = await this.prisma.users.update({
where: { id: userId },
data: { twoFactorEnabled: true },
});
return this.prisma.expose<users>(result);
}

async loginWithTotp(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
Body,
Controller,
Delete,
Param,
ParseIntPipe,
Post,
} from '@nestjs/common';
import { users } from '@prisma/client';
import { Expose } from 'src/modules/prisma/prisma.interface';
import { Scopes } from '../auth/scope.decorator';
import { EnableTwoFactorAuthenticationDto } from './multi-factor-authentication.dto';
import { MultiFactorAuthenticationService } from './multi-factor-authentication.service';

@Controller('users/:userId/multi-factor-authentication')
export class MultiFactorAuthenticationController {
constructor(
private multiFactorAuthenticationService: MultiFactorAuthenticationService,
) {}

@Post('2fa')
@Scopes('user-{userId}:write-2fa')
async enable2FA(
@Param('userId', ParseIntPipe) userId: number,
@Body() body: EnableTwoFactorAuthenticationDto,
): Promise<Expose<users> | string> {
if (body.token)
return this.multiFactorAuthenticationService.enableTwoFactorAuthentication(
userId,
body.token,
);
return this.multiFactorAuthenticationService.requestTwoFactorAuthentication(
userId,
);
}

@Delete('2fa')
@Scopes('user-{userId}:delete-2fa')
async disable2FA(
@Param('userId', ParseIntPipe) userId: number,
): Promise<Expose<users>> {
return this.multiFactorAuthenticationService.disableTwoFactorAuthentication(
userId,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IsOptional, IsString } from 'class-validator';

export class EnableTwoFactorAuthenticationDto {
@IsString()
@IsOptional()
token?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { AuthModule } from '../auth/auth.module';
import { PrismaModule } from '../prisma/prisma.module';
import { MultiFactorAuthenticationController } from './multi-factor-authentication.controller';
import { MultiFactorAuthenticationService } from './multi-factor-authentication.service';

@Module({
imports: [PrismaModule, AuthModule],
controllers: [MultiFactorAuthenticationController],
providers: [MultiFactorAuthenticationService],
})
export class MultiFactorAuthenticationModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { users } from '@prisma/client';
import { AuthService } from '../auth/auth.service';
import { Expose } from '../prisma/prisma.interface';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class MultiFactorAuthenticationService {
constructor(private prisma: PrismaService, private auth: AuthService) {}

async requestTwoFactorAuthentication(userId: number): Promise<string> {
const enabled = await this.prisma.users.findOne({
where: { id: userId },
select: { twoFactorEnabled: true },
});
if (enabled.twoFactorEnabled)
throw new BadRequestException(
'Two-factor authentication is already enabled',
);
return this.auth.getTotpQrCode(userId);
}

async enableTwoFactorAuthentication(
userId: number,
token: string,
): Promise<Expose<users>> {
return this.auth.enableTotp(userId, token);
}

async disableTwoFactorAuthentication(userId: number): Promise<Expose<users>> {
const enabled = await this.prisma.users.findOne({
where: { id: userId },
select: { twoFactorEnabled: true },
});
if (!enabled.twoFactorEnabled)
throw new BadRequestException('Two-factor authentication is not enabled');
const user = await this.prisma.users.update({
where: { id: userId },
data: { twoFactorEnabled: false, twoFactorSecret: null },
});
return this.prisma.expose<users>(user);
}
}

0 comments on commit cee3a55

Please sign in to comment.