Skip to content

Commit

Permalink
✨ Add 2FA disable endpoint (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
usatie authored Feb 16, 2024
1 parent 45a9252 commit 2837e6a
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 2 deletions.
14 changes: 12 additions & 2 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
Post,
Expand All @@ -16,14 +17,14 @@ import {
ApiTags,
} from '@nestjs/swagger';
import type { User } from '@prisma/client';
import { Response } from 'express';
import { CurrentUser } from 'src/common/decorators/current-user.decorator';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { TwoFactorAuthenticationDto } from './dto/twoFactorAuthentication.dto';
import { TwoFactorAuthenticationEnableDto } from './dto/twoFactorAuthenticationEnable.dto';
import { AuthEntity } from './entity/auth.entity';
import { JwtGuardWithout2FA } from './jwt-auth.guard';
import { Response } from 'express';
import { JwtAuthGuard, JwtGuardWithout2FA } from './jwt-auth.guard';

const constants = {
loginUrl: ((): string => {
Expand Down Expand Up @@ -121,4 +122,13 @@ export class AuthController {
) {
return this.authService.twoFactorAuthenticate(dto, user.id);
}

@Delete('2fa/disable')
@HttpCode(200)
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse()
async disable2FA(@CurrentUser() user: User) {
return this.authService.disableTwoFactorAuthentication(user.id);
}
}
15 changes: 15 additions & 0 deletions backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,21 @@ export class AuthService {
});
}

disableTwoFactorAuthentication(userId: number) {
return this.prisma.$transaction(async (prisma) => {
const user = await prisma.user.findUnique({ where: { id: userId } });
if (!user.twoFactorEnabled) {
throw new ConflictException('2FA secret is not enabled');
}
await prisma.user.update({
where: { id: user.id },
data: {
twoFactorEnabled: false,
},
});
});
}

async twoFactorAuthenticate(dto: TwoFactorAuthenticationDto, userId: number) {
const user = await this.prisma.user.findUnique({ where: { id: userId } });
if (!user.twoFactorEnabled) {
Expand Down
11 changes: 11 additions & 0 deletions backend/test/auth.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,16 @@ describe('AuthController (e2e)', () => {
it('[GET /user/me] should return 200 if 2FA is enabled and code is provided', async () => {
await app.getMe(user.accessToken).expect(200);
});

it('[DELETE /auth/2fa/disable] should disable 2FA', async () => {
await app.disableTwoFactorAuthentication(user.accessToken).expect(200);
});

it('[POST /auth/2fa/enable] should re-enable 2FA', async () => {
const code = authenticator.generate(secret);
await app
.enableTwoFactorAuthentication(code, user.accessToken)
.expect(200);
});
});
});
5 changes: 5 additions & 0 deletions backend/test/utils/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export class TestApp {
.set('Authorization', `Bearer ${accessToken}`)
.send({ code });

disableTwoFactorAuthentication = (accessToken: string) =>
request(this.app.getHttpServer())
.delete('/auth/2fa/disable')
.set('Authorization', `Bearer ${accessToken}`);

twoFactorAuthenticate = (code: string, accessToken: string) =>
request(this.app.getHttpServer())
.post('/auth/2fa/authenticate')
Expand Down

0 comments on commit 2837e6a

Please sign in to comment.