131 lines
3.0 KiB
TypeScript

import {
BadRequestException,
Body,
Controller,
Get,
HttpCode,
ParseBoolPipe,
Post,
Request,
UnauthorizedException,
UseGuards
} from '@nestjs/common';
import { AuthService } from '../services/auth';
import { AuthGuard } from '@nestjs/passport';
import { Public } from '../authguards';
import { Responses } from 'dto';
import { tfaTypes } from '../entities';
import { toDataURL } from 'qrcode';
import * as base32 from 'thirty-two';
@Controller('api/auth')
export default class AuthController {
constructor(private authService: AuthService) {}
@Public()
@UseGuards(AuthGuard('local'))
@Post('login')
@HttpCode(200)
async login(
@Request() req,
@Body('otp') otp?: string
): Promise<
Responses.Auth.LoginResponse | Responses.Auth.TfaRequiredResponse
> {
if (this.authService.requiresTfa(req.user)) {
if (!otp) {
if (req.user.tfaType == tfaTypes.EMAIL)
await this.authService.sendTfaMail(req.user);
return {
statusCode: 200
};
}
if (!(await this.authService.verifyTfa(req.user, otp)))
throw new UnauthorizedException('Incorrect 2fa');
}
return {
statusCode: 200,
jwt: await this.authService.login(req.user)
};
}
async tfa(
req,
code: string,
type: tfaTypes
): Promise<Responses.Auth.TfaCompletedResponse> {
if (!(await this.authService.verifyTfa(req.user, code, type))) {
throw new UnauthorizedException('Incorrect 2fa');
}
await this.authService.setTfaType(req.user, type);
await this.authService.revokeAll(req.user);
return {
statusCode: 200
};
}
@Post('2fa/complete/mail')
async tfaMail(
@Request() req,
@Body('code') code: string
): Promise<Responses.Auth.TfaCompletedResponse> {
return await this.tfa(req, code, tfaTypes.EMAIL);
}
@Post('2fa/complete/totp')
async tfaTotp(
@Request() req,
@Body('code') code: string
): Promise<Responses.Auth.TfaCompletedResponse> {
return await this.tfa(req, code, tfaTypes.TOTP);
}
@Get('2fa/setup')
async setupTotp(
@Request() req,
@Body('mail', ParseBoolPipe) mail: boolean
): Promise<
| Responses.Auth.RequestTotpTfaResponse
| Responses.Auth.RequestEmailTfaResponse
> {
const secret = await this.authService.setupTfa(req.user);
if (mail)
return {
statusCode: 200
};
return {
statusCode: 200,
qrCode: await toDataURL(
`otpauth://totp/MFileserver:${req.user.name}?secret=${base32
.encode(secret)
.toString()}&issuer=MFileserver`
),
secret
};
}
@Public()
@Post('signup')
async signup(
@Body('username') username,
@Body('password') password
): Promise<Responses.Auth.SignupResponse> {
if ((await this.authService.findUser(username)) != null)
throw new BadRequestException('Username already taken');
await this.authService.signup(username, password);
return {
statusCode: 200
};
}
@Post('refresh')
async refresh(@Request() req): Promise<Responses.Auth.RefreshResponse> {
const token = await this.authService.login(req.user);
await this.authService.revoke(req.token);
return {
statusCode: 200,
jwt: token
};
}
}