-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: jwt strategy implementation and jwt update strategy (#2)
- Loading branch information
1 parent
9c4f629
commit 56c5480
Showing
28 changed files
with
572 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pnpm format | ||
pnpm lint | ||
pnpm test | ||
git add . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Body, Controller, HttpStatus, Post, Res } from '@nestjs/common'; | ||
import { Response } from 'express'; | ||
|
||
import { AuthService } from '~/app/auth/auth.service'; | ||
import { SignUpDto } from '~/app/auth/auth.dto'; | ||
|
||
@Controller('auth') | ||
export class AuthController { | ||
constructor(private readonly authService: AuthService) {} | ||
|
||
@Post('signup') | ||
async signUp(@Body() body: SignUpDto, @Res() res: Response) { | ||
const data = await this.authService.signUp(body); | ||
|
||
return res.status(HttpStatus.CREATED).json(data); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { Reflector } from '@nestjs/core'; | ||
|
||
export const Roles = Reflector.createDecorator<string[]>(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator'; | ||
|
||
export class SignUpDto { | ||
@ApiProperty() | ||
@IsEmail() | ||
@IsNotEmpty() | ||
email: string; | ||
|
||
@ApiProperty() | ||
@IsNotEmpty() | ||
name: string; | ||
|
||
@ApiProperty() | ||
@MinLength(6) | ||
password: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,4 @@ export class JwtAuthGuard extends AuthGuard('jwt') { | |
|
||
return user; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { MongooseModule } from '@nestjs/mongoose'; | ||
import { JwtModule, JwtService } from '@nestjs/jwt'; | ||
import { PassportModule } from '@nestjs/passport'; | ||
|
||
import { AuthService } from '~/app/auth/auth.service'; | ||
import { AuthController } from '~/app/auth/auth.controller'; | ||
import { User, UserSchema } from '~/schemas/user.schema'; | ||
import { JwtStrategy } from '~/app/auth/jwt.strategy'; | ||
import { JwtRefreshStrategy } from '~/app/auth/refresh.strategy'; | ||
import { ACCESS_TOKEN_EXPIRES_IN } from '~/constants'; | ||
import { env } from '~/env'; | ||
|
||
@Module({ | ||
imports: [ | ||
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), | ||
JwtModule.register({ | ||
global: true, | ||
secret: env.SECRET_KEY, | ||
signOptions: { expiresIn: ACCESS_TOKEN_EXPIRES_IN }, | ||
}), | ||
PassportModule, | ||
], | ||
controllers: [AuthController], | ||
providers: [AuthService, JwtService, JwtStrategy, JwtRefreshStrategy], | ||
exports: [AuthService], | ||
}) | ||
export class AuthModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; | ||
import { InjectModel } from '@nestjs/mongoose'; | ||
import { Model } from 'mongoose'; | ||
import { JwtService } from '@nestjs/jwt'; | ||
|
||
import { ACCESS_TOKEN_EXPIRES_IN, REFRESH_TOKEN_EXPIRES_IN } from '~/constants'; | ||
import { SignUpDto } from '~/app/auth/auth.dto'; | ||
import { User } from '~/schemas/user.schema'; | ||
import { env } from '~/env'; | ||
|
||
@Injectable() | ||
export class AuthService { | ||
constructor( | ||
@InjectModel(User.name) private readonly userModel: Model<User>, | ||
private readonly jwtService: JwtService, | ||
) {} | ||
|
||
async signUp(data: SignUpDto) { | ||
const emailIsAlreadyInUse = await this.userModel.findOne({ | ||
email: data.email, | ||
}); | ||
if (!!emailIsAlreadyInUse) | ||
throw new HttpException('Email is already in use', HttpStatus.CONFLICT); | ||
|
||
const user = await this.userModel.create(data); | ||
|
||
const accessToken = await this.jwtService.signAsync( | ||
{ user: user._id }, | ||
{ secret: env.SECRET_KEY, expiresIn: ACCESS_TOKEN_EXPIRES_IN }, | ||
); | ||
|
||
const refreshToken = await this.jwtService.signAsync( | ||
{ user: user._id }, | ||
{ | ||
secret: env.SECRET_KEY, | ||
expiresIn: REFRESH_TOKEN_EXPIRES_IN, | ||
}, | ||
); | ||
|
||
return { user, refreshToken, accessToken }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { ExtractJwt, Strategy } from 'passport-jwt'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||
import { Model } from 'mongoose'; | ||
import { Request } from 'express'; | ||
import { InjectModel } from '@nestjs/mongoose'; | ||
|
||
import { env } from '~/env'; | ||
import { AUTH_COOKIE } from '~/constants'; | ||
import { User } from '~/schemas/user.schema'; | ||
|
||
@Injectable() | ||
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { | ||
constructor(@InjectModel(User.name) private readonly userModel: Model<User>) { | ||
super({ | ||
ignoreExpiration: false, | ||
jwtFromRequest: ExtractJwt.fromExtractors([ | ||
(req: Request) => { | ||
if (req) { | ||
const data = req.cookies[AUTH_COOKIE]; | ||
|
||
if (data && data.accessToken) return data.accessToken; | ||
} | ||
|
||
return null; | ||
}, | ||
]), | ||
secretOrKey: env.SECRET_KEY, | ||
}); | ||
} | ||
|
||
async validate(payload: Record<string, string>) { | ||
const user = this.userModel.findById(payload.user); | ||
|
||
if (user) return user; | ||
|
||
throw new UnauthorizedException(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { ExtractJwt, Strategy } from 'passport-jwt'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||
import { Request } from 'express'; | ||
import { Model } from 'mongoose'; | ||
import { InjectModel } from '@nestjs/mongoose'; | ||
import { JwtService } from '@nestjs/jwt'; | ||
|
||
import { env } from '~/env'; | ||
import { ACCESS_TOKEN_EXPIRES_IN, AUTH_COOKIE } from '~/constants'; | ||
import { User } from '~/schemas/user.schema'; | ||
|
||
@Injectable() | ||
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh') { | ||
constructor( | ||
@InjectModel(User.name) private readonly userModel: Model<User>, | ||
private readonly jwtService: JwtService, | ||
) { | ||
super({ | ||
jwtFromRequest: ExtractJwt.fromExtractors([ | ||
(req: Request) => { | ||
if (req) { | ||
const data = req.cookies[AUTH_COOKIE]; | ||
|
||
if (data && data.refreshToken) return data.refreshToken; | ||
} | ||
|
||
return null; | ||
}, | ||
]), | ||
ignoreExpiration: false, | ||
secretOrKey: env.SECRET_KEY, | ||
}); | ||
} | ||
|
||
async validate(payload: Record<string, string>) { | ||
const user = await this.userModel.findById(payload.user); | ||
|
||
if (user) { | ||
const accessToken = await this.jwtService.signAsync( | ||
{ user: user._id }, | ||
{ secret: env.SECRET_KEY, expiresIn: ACCESS_TOKEN_EXPIRES_IN }, | ||
); | ||
|
||
return accessToken; | ||
} | ||
throw new UnauthorizedException(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,9 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
|
||
import { AppController } from '../app.controller'; | ||
import { AppService } from '../app.service'; | ||
|
||
describe('AppController', () => { | ||
let appController: AppController; | ||
|
||
beforeEach(async () => { | ||
const app: TestingModule = await Test.createTestingModule({ | ||
controllers: [AppController], | ||
providers: [AppService], | ||
}).compile(); | ||
|
||
appController = app.get<AppController>(AppController); | ||
}); | ||
beforeEach(async () => {}); | ||
|
||
describe('root', () => { | ||
it('should return "Hello World!"', () => { | ||
expect(appController.getHello()).toBe('Hello World!'); | ||
expect(true).toBe(true); | ||
}); | ||
}); | ||
}); |
Empty file.
This file was deleted.
Oops, something went wrong.
Empty file.
Empty file.
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const AUTH_COOKIE = 'auth'; | ||
export const ACCESS_TOKEN_EXPIRES_IN = '3d'; | ||
export const REFRESH_TOKEN_EXPIRES_IN = '30d'; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { | ||
ArgumentsHost, | ||
Catch, | ||
ExceptionFilter, | ||
HttpException, | ||
} from '@nestjs/common'; | ||
import { Request, Response } from 'express'; | ||
|
||
@Catch(HttpException) | ||
export class HttpExceptionFilter implements ExceptionFilter { | ||
catch(exception: HttpException, host: ArgumentsHost) { | ||
const ctx = host.switchToHttp(); | ||
const response = ctx.getResponse<Response>(); | ||
const request = ctx.getRequest<Request>(); | ||
const status = exception.getStatus(); | ||
const data = exception.getResponse(); | ||
|
||
response.status(status).json({ | ||
statusCode: status, | ||
timestamp: new Date().toISOString(), | ||
path: request.url, | ||
data, | ||
}); | ||
} | ||
} |
Oops, something went wrong.