Skip to content

Commit a1b1c02

Browse files
authored
Merge pull request #58 from techknowledge-blog/feature/user-auth
User Auth
2 parents c98fdfd + b23a9b3 commit a1b1c02

17 files changed

+2577
-272
lines changed

package-lock.json

Lines changed: 2399 additions & 191 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app.module.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@ import { AppService } from './app.service';
44
import { PostsService } from './modules/posts/posts.service';
55
import { PrismaService } from './prisma.service';
66
import { PostsModule } from './modules/posts/posts.module';
7-
import { AuthModule } from './modules/auth/auth.module';
87
import { UsersModule } from './modules/users/users.module';
98
import { UsersController } from './modules/users/users.controller';
109
import { UsersService } from './modules/users/users.service';
10+
import { AuthModule } from './modules/auth/auth.module';
11+
import { AuthService } from './modules/auth/auth.service';
12+
import { AuthController } from './modules/auth/auth.controller';
1113

1214
@Module({
1315
imports: [PostsModule, AuthModule, UsersModule],
14-
controllers: [AppController, UsersController],
15-
providers: [AppService, PrismaService, PostsService, UsersService],
16+
controllers: [AppController, UsersController, AuthController],
17+
providers: [
18+
AppService,
19+
PrismaService,
20+
PostsService,
21+
UsersService,
22+
AuthService,
23+
],
1624
exports: [PrismaService],
1725
})
1826
export class AppModule {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { AuthController } from '../auth/auth.controller';
3+
4+
describe('AuthController', () => {
5+
let controller: AuthController;
6+
7+
beforeEach(async () => {
8+
const module: TestingModule = await Test.createTestingModule({
9+
controllers: [AuthController],
10+
}).compile();
11+
12+
controller = module.get<AuthController>(AuthController);
13+
});
14+
15+
it('should be defined', () => {
16+
expect(controller).toBeDefined();
17+
});
18+
});
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
1+
import {
2+
Body,
3+
Controller,
4+
Get,
5+
Post,
6+
Request,
7+
UseGuards,
8+
} from '@nestjs/common';
29
import { AuthService } from './auth.service';
10+
import { AuthGuard } from './guards/auth.guard';
311

412
@Controller('auth')
513
export class AuthController {
614
constructor(private authService: AuthService) {}
715

816
@Post('login')
9-
async login(@Body() body: { username: string; password: string }) {
10-
const user = await this.authService.validateUser(body.username, body.password);
11-
if (!user) {
12-
throw new UnauthorizedException('Invalid credentials');
13-
}
14-
return this.authService.login(user);
17+
login(@Body() input: { username: string; password: string }) {
18+
return this.authService.authenticate(input);
19+
}
20+
21+
@UseGuards(AuthGuard)
22+
@Get('profile')
23+
getUserInfo(@Request() request) {
24+
return request.user;
1525
}
1626
}

src/modules/auth/auth.module.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import { Module } from '@nestjs/common';
2-
import { PassportModule } from '@nestjs/passport';
3-
import { JwtModule } from '@nestjs/jwt';
4-
import { PrismaService } from 'src/prisma.service';
5-
import { AuthController } from './auth.controller';
62
import { AuthService } from './auth.service';
7-
import { JwtStrategy } from './jwt.strategy';
3+
import { AuthController } from './auth.controller';
4+
import { UsersModule } from '../users/users.module';
5+
import { PrismaService } from 'src/prisma.service';
6+
import { JwtModule } from '@nestjs/jwt';
7+
import { JWT_SECRET } from '../config/jwt-secret';
8+
import { JwtStrategy } from './strategy/jwt.strategy';
89

910
@Module({
11+
providers: [AuthService, PrismaService, JwtStrategy],
12+
controllers: [AuthController],
1013
imports: [
11-
PassportModule.register({ defaultStrategy: 'jwt' }),
14+
UsersModule,
1215
JwtModule.register({
13-
secret: 'secretKey',
14-
signOptions: { expiresIn: '1h' },
16+
global: true,
17+
secret: JWT_SECRET,
18+
signOptions: { expiresIn: '1d' },
1519
}),
1620
],
17-
controllers: [AuthController],
18-
providers: [AuthService, PrismaService, JwtStrategy],
19-
exports: [AuthService],
2021
})
2122
export class AuthModule {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { AuthService } from './auth.service';
3+
4+
describe('AuthService', () => {
5+
let service: AuthService;
6+
7+
beforeEach(async () => {
8+
const module: TestingModule = await Test.createTestingModule({
9+
providers: [AuthService],
10+
}).compile();
11+
12+
service = module.get<AuthService>(AuthService);
13+
});
14+
15+
it('should be defined', () => {
16+
expect(service).toBeDefined();
17+
});
18+
});

src/modules/auth/auth.service.ts

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,61 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { Injectable, UnauthorizedException } from '@nestjs/common';
2+
import { UsersService } from '../users/users.service';
23
import { JwtService } from '@nestjs/jwt';
3-
import * as bcrypt from 'bcryptjs';
4-
import { PrismaService } from 'src/prisma.service';
4+
5+
type AuthInput = { username: string; password: string };
6+
type SignInData = { userId: number; username: string; role: string };
7+
type AuthResult = {
8+
accessToken: string;
9+
userId: number;
10+
username: string;
11+
role: string;
12+
};
513

614
@Injectable()
715
export class AuthService {
816
constructor(
17+
private usersService: UsersService,
918
private jwtService: JwtService,
10-
private prismaService: PrismaService,
1119
) {}
1220

13-
async validateUser(username: string, password: string): Promise<any> {
14-
const user = await this.prismaService.user.findFirst({
15-
where: { name: username },
16-
});
21+
async authenticate(input: AuthInput): Promise<AuthResult> {
22+
const user = await this.validateUser(input);
1723

1824
if (!user) {
19-
return null;
25+
throw new UnauthorizedException();
2026
}
2127

22-
// const isMatch = await bcrypt.compare(password, user.passwordHash);
23-
const isMatch = password == user.passwordHash;
24-
if (isMatch) {
25-
const { passwordHash, ...result } = user;
26-
return result;
28+
return this.signIn(user);
29+
}
30+
31+
async validateUser(input: AuthInput): Promise<SignInData | null> {
32+
const user = await this.usersService.findUserByName(input.username);
33+
34+
if (user && user.passwordHash === input.password) {
35+
return {
36+
userId: user.id,
37+
username: user.name,
38+
role: user.role,
39+
};
2740
}
2841

2942
return null;
3043
}
3144

32-
async login(user: any) {
33-
const payload = { username: user.name, sub: user.id, role: user.role };
45+
async signIn(user: SignInData): Promise<AuthResult> {
46+
const tokenPayload = {
47+
sub: user.userId,
48+
username: user.username,
49+
role: user.role,
50+
};
51+
52+
const accessToken = await this.jwtService.signAsync(tokenPayload);
53+
3454
return {
35-
access_token: this.jwtService.sign(payload),
55+
accessToken,
56+
username: user.username,
57+
userId: user.userId,
58+
role: user.role,
3659
};
3760
}
3861
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
CanActivate,
3+
ExecutionContext,
4+
Injectable,
5+
UnauthorizedException,
6+
} from '@nestjs/common';
7+
import { JwtService } from '@nestjs/jwt';
8+
9+
@Injectable()
10+
export class AuthGuard implements CanActivate {
11+
constructor(private jwtService: JwtService) {}
12+
13+
async canActivate(context: ExecutionContext) {
14+
const request = context.switchToHttp().getRequest();
15+
const authorization = request.headers.authorization;
16+
const token = authorization?.split(' ')[1];
17+
18+
if (!token) {
19+
throw new UnauthorizedException();
20+
}
21+
22+
try {
23+
const tokenPayload = await this.jwtService.verifyAsync(token);
24+
request.user = {
25+
userId: tokenPayload.sub,
26+
username: tokenPayload.username,
27+
role: tokenPayload.role,
28+
};
29+
return true;
30+
} catch (error) {
31+
throw new UnauthorizedException();
32+
}
33+
}
34+
}
File renamed without changes.

src/modules/auth/roles.decorator.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)