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

Commit

Permalink
✨ Add tokens module, 2FA
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Oct 27, 2020
1 parent 6877265 commit e362889
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PassportModule } from '@nestjs/passport';
import { EmailModule } from '../email/email.module';
import { PrismaModule } from '../prisma/prisma.module';
import { PwnedModule } from '../pwned/pwned.module';
import { TokensModule } from '../tokens/tokens.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
Expand All @@ -14,6 +15,7 @@ import { JwtStrategy } from './jwt.strategy';
PassportModule.register({ defaultStrategy: 'jwt' }),
PrismaModule,
EmailModule,
TokensModule,
ConfigModule,
PwnedModule,
JwtModule.register({
Expand Down
22 changes: 19 additions & 3 deletions src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { PrismaService } from '../prisma/prisma.service';
import { PwnedService } from '../pwned/pwned.service';
import { RegisterDto } from './auth.dto';
import { AccessTokenClaims } from './auth.interface';
import { TokensService } from '../tokens/tokens.service';
import { TWO_FACTOR_TOKEN } from '../tokens/tokens.constants';

@Injectable()
export class AuthService {
Expand All @@ -30,6 +32,7 @@ export class AuthService {
private configService: ConfigService,
private jwtService: JwtService,
private pwnedService: PwnedService,
private tokensService: TokensService,
) {
authenticator.options.window = [
this.configService.get<number>('security.totpWindowPast'),
Expand Down Expand Up @@ -64,7 +67,7 @@ export class AuthService {
) {
const id = await this.validateUser(email, password);
if (!id) throw new UnauthorizedException();
if (code) return this.loginWithTotp(ipAddress, userAgent, id, code);
if (code) return this.loginUserWithTotpCode(ipAddress, userAgent, id, code);
return this.loginResponse(ipAddress, userAgent, id);
}

Expand Down Expand Up @@ -190,9 +193,22 @@ export class AuthService {
userAgent: string,
token: string,
code: string,
) {
const { id } = this.tokensService.verify<{ id: number }>(
TWO_FACTOR_TOKEN,
token,
);
return this.loginUserWithTotpCode(ipAddress, userAgent, id, code);
}

private async loginUserWithTotpCode(
ipAddress: string,
userAgent: string,
id: number,
code: string,
) {
const user = await this.prisma.users.findOne({
where: { id: userId },
where: { id },
select: { twoFactorSecret: true, twoFactorEnabled: true },
});
if (!user) throw new NotFoundException();
Expand All @@ -202,7 +218,7 @@ export class AuthService {
throw new UnauthorizedException(
'Two-factor authentication code is invalid',
);
return this.loginResponse(ipAddress, userAgent, userId);
return this.loginResponse(ipAddress, userAgent, id);
}

private async getAccessToken(userId: number): Promise<string> {
Expand Down
1 change: 1 addition & 0 deletions src/modules/tokens/tokens.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TWO_FACTOR_TOKEN = 'TWO_FACTOR_TOKEN';
10 changes: 10 additions & 0 deletions src/modules/tokens/tokens.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TokensService } from './tokens.service';

@Module({
imports: [ConfigModule],
providers: [TokensService],
exports: [TokensService],
})
export class TokensModule {}
45 changes: 45 additions & 0 deletions src/modules/tokens/tokens.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { v4 } from 'uuid';
import {
sign,
decode,
verify,
SignOptions,
VerifyOptions,
DecodeOptions,
} from 'jsonwebtoken';

@Injectable()
export class TokensService {
constructor(private configService: ConfigService) {}

signJwt(
subject: string,
payload: string | object | Buffer,
expiresIn?: string,
options?: SignOptions,
) {
return sign(payload, this.configService.get<string>('security.jwtSecret'), {
...options,
subject,
expiresIn,
});
}

verify<T>(subject: string, token: string, options?: VerifyOptions) {
return (verify(
token,
this.configService.get<string>('security.jwtSecret'),
{ ...options, subject },
) as any) as T;
}

decode<T>(token: string, options?: DecodeOptions) {
return decode(token, options) as T;
}

generateUuid() {
return v4();
}
}

0 comments on commit e362889

Please sign in to comment.