Skip to content

Commit 16178bf

Browse files
committed
Adding the ability to guard APIs using JWT
1 parent 2d5d234 commit 16178bf

File tree

11 files changed

+140
-0
lines changed

11 files changed

+140
-0
lines changed

server/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@
2929
"@nestjs/common": "^7.5.1",
3030
"@nestjs/config": "^0.6.3",
3131
"@nestjs/core": "^7.5.1",
32+
"@nestjs/jwt": "^8.0.0",
33+
"@nestjs/passport": "^8.2.1",
3234
"@nestjs/platform-express": "^7.5.1",
3335
"@nestjs/typeorm": "^7.1.5",
3436
"@types/bcrypt": "^3.0.1",
3537
"bcrypt": "^5.0.1",
3638
"class-transformer": "^0.3.2",
3739
"class-validator": "^0.13.1",
3840
"mysql2": "^2.3.3",
41+
"passport": "^0.5.2",
42+
"passport-jwt": "^4.0.0",
43+
"passport-local": "^1.0.0",
3944
"reflect-metadata": "^0.1.13",
4045
"rimraf": "^3.0.2",
4146
"rxjs": "^6.6.3",
@@ -48,6 +53,9 @@
4853
"@types/express": "^4.17.8",
4954
"@types/jest": "^26.0.15",
5055
"@types/node": "^14.14.6",
56+
"@types/passport": "^1.0.7",
57+
"@types/passport-jwt": "^3.0.6",
58+
"@types/passport-local": "^1.0.34",
5159
"@types/supertest": "^2.0.10",
5260
"@typescript-eslint/eslint-plugin": "^4.6.1",
5361
"@typescript-eslint/parser": "^4.6.1",

server/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { AppService } from './app.service';
66
import { typeOrmAsyncConfig } from './config/typeorm.config';
77
import { QuizModule } from './modules/quiz/quiz.module';
88
import { UserModule } from './modules/user/user.module';
9+
import { AuthModule } from './modules/auth/auth.module';
910
@Module({
1011
imports: [
1112
ConfigModule.forRoot({ isGlobal: true }),
1213
TypeOrmModule.forRootAsync(typeOrmAsyncConfig),
1314
QuizModule,
1415
UserModule,
16+
AuthModule,
1517
],
1618
controllers: [AppController],
1719
providers: [AppService],
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Controller, Get, Post, Request, UseGuards } from '@nestjs/common';
2+
import { AuthService } from './auth.service';
3+
import { JwtAuthGuard } from './jwt-auth.guard';
4+
import { LocalAuthGuard } from './local-auth.guard';
5+
6+
@Controller('auth')
7+
export class AuthController {
8+
constructor(private authService: AuthService) {}
9+
10+
@UseGuards(LocalAuthGuard)
11+
@Post('login')
12+
async login(@Request() req): Promise<any> {
13+
return this.authService.generateToken(req.user);
14+
}
15+
16+
@UseGuards(JwtAuthGuard)
17+
@Get('user')
18+
async user(@Request() req): Promise<any> {
19+
return req.user;
20+
}
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Module } from '@nestjs/common';
2+
import { AuthService } from './auth.service';
3+
import { AuthController } from './auth.controller';
4+
import { LocalStrategy } from './local.strategy';
5+
import { UserModule } from '../user/user.module';
6+
import { PassportModule } from '@nestjs/passport';
7+
import { JwtModule } from '@nestjs/jwt';
8+
import { JwtStrategy } from './jwt.strategy';
9+
10+
@Module({
11+
imports: [
12+
UserModule,
13+
PassportModule,
14+
JwtModule.register({
15+
secret: 'asdasghgfhgfh5355dfgfg345345',
16+
signOptions: { expiresIn: '1d' },
17+
}),
18+
],
19+
providers: [AuthService, LocalStrategy, JwtStrategy],
20+
controllers: [AuthController],
21+
})
22+
export class AuthModule {}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {
2+
BadRequestException,
3+
Injectable,
4+
UnauthorizedException,
5+
} from '@nestjs/common';
6+
import { UserService } from '../user/user.service';
7+
import * as bcrypt from 'bcrypt';
8+
import { JwtService } from '@nestjs/jwt';
9+
10+
@Injectable()
11+
export class AuthService {
12+
constructor(
13+
private userService: UserService,
14+
private jwtService: JwtService,
15+
) {}
16+
17+
async validateUserCreds(email: string, password: string): Promise<any> {
18+
const user = await this.userService.getUserByEmail(email);
19+
20+
if (!user) throw new BadRequestException();
21+
22+
if (!(await bcrypt.compare(password, user.password)))
23+
throw new UnauthorizedException();
24+
25+
return user;
26+
}
27+
28+
generateToken(user: any) {
29+
return {
30+
access_token: this.jwtService.sign({
31+
name: user.name,
32+
sub: user.id,
33+
}),
34+
};
35+
}
36+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { AuthGuard } from '@nestjs/passport';
3+
4+
@Injectable()
5+
export class JwtAuthGuard extends AuthGuard('jwt') {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { PassportStrategy } from '@nestjs/passport';
2+
import { ExtractJwt, Strategy } from 'passport-jwt';
3+
4+
export class JwtStrategy extends PassportStrategy(Strategy) {
5+
constructor() {
6+
super({
7+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
8+
secretOrKey: 'asdasghgfhgfh5355dfgfg345345',
9+
});
10+
}
11+
12+
async validate(payload: any) {
13+
return {
14+
id: payload.sub,
15+
name: payload.name,
16+
tenant: 'amitav',
17+
};
18+
}
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { AuthGuard } from '@nestjs/passport';
3+
4+
@Injectable()
5+
export class LocalAuthGuard extends AuthGuard('local') {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Injectable, UnauthorizedException } from '@nestjs/common';
2+
import { PassportStrategy } from '@nestjs/passport';
3+
import { Strategy } from 'passport-local';
4+
import { AuthService } from './auth.service';
5+
6+
@Injectable()
7+
export class LocalStrategy extends PassportStrategy(Strategy) {
8+
constructor(private authService: AuthService) {
9+
super();
10+
}
11+
12+
async validate(email: string, password: string) {
13+
const user = await this.authService.validateUserCreds(email, password);
14+
if (!user) throw new UnauthorizedException();
15+
return user;
16+
}
17+
}

server/src/modules/user/user.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ import { UserService } from './user.service';
55
@Module({
66
providers: [UserService],
77
controllers: [UserController],
8+
exports: [UserService],
89
})
910
export class UserModule {}

server/src/modules/user/user.service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ export class UserService {
1414

1515
return await user.save();
1616
}
17+
18+
async getUserByEmail(email: string): Promise<User | undefined> {
19+
return User.findOne({ where: { email } });
20+
}
1721
}

0 commit comments

Comments
 (0)