131 lines
3.0 KiB
TypeScript
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
|
|
};
|
|
}
|
|
}
|