- Access/Refresh 토큰 검증 및 허가 구현 중
This commit is contained in:
2
.env.dev
2
.env.dev
@@ -10,5 +10,5 @@ PG_DATABASE_URL=postgres://baekyangdan:qwas745478!@bkdhome.p-e.kr:15454/schedule
|
|||||||
|
|
||||||
# Redis 설정
|
# Redis 설정
|
||||||
RD_HOST=bkdhome.p-e.kr
|
RD_HOST=bkdhome.p-e.kr
|
||||||
RD_PORT=6779
|
RD_PORT=16779
|
||||||
RD_URL=redis://bkdhome.p-e.kr:16779
|
RD_URL=redis://bkdhome.p-e.kr:16779
|
||||||
|
|||||||
5
src/common/decorators/public.decorator.ts
Normal file
5
src/common/decorators/public.decorator.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { SetMetadata } from "@nestjs/common";
|
||||||
|
|
||||||
|
export const IS_PUBLIC_KEY = 'isPublic345827';
|
||||||
|
|
||||||
|
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
||||||
42
src/const/HttpResponse.ts
Normal file
42
src/const/HttpResponse.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
export const HttpResponse: Record<string, {code: number, title: string, message: string}> = {
|
||||||
|
"ACCESS_TOKEN_EXPIRED": {
|
||||||
|
code: 401,
|
||||||
|
title: "ACCESS_TOKEN_EXPIRED",
|
||||||
|
message: "ACCESS TOKEN EXPIRED"
|
||||||
|
},
|
||||||
|
"INVALID_TOKEN": {
|
||||||
|
code: 401,
|
||||||
|
title: "INVALID_TOKEN",
|
||||||
|
message: "INVALID TOKEN"
|
||||||
|
},
|
||||||
|
"REFRESH_TOKEN_EXPIRED": {
|
||||||
|
code: 401,
|
||||||
|
title: "REFRESH_TOKEN_EXPIRED",
|
||||||
|
message: "REFRESH TOKEN EXPIRED"
|
||||||
|
},
|
||||||
|
"UNAUTHORIZED": {
|
||||||
|
code: 401,
|
||||||
|
title: "UNAUTHORIZED",
|
||||||
|
message: "UNAUTHORIZED"
|
||||||
|
},
|
||||||
|
"OK": {
|
||||||
|
code: 200,
|
||||||
|
title: "OK",
|
||||||
|
message: "OK"
|
||||||
|
},
|
||||||
|
"CREATED": {
|
||||||
|
code: 201,
|
||||||
|
title: "CREATED",
|
||||||
|
message: "CREATED"
|
||||||
|
},
|
||||||
|
"BAD_REQUEST": {
|
||||||
|
code: 400,
|
||||||
|
title: "BAD_REQUEST",
|
||||||
|
message: "BAD REQUEST"
|
||||||
|
},
|
||||||
|
"INTERNAL_SERVER_ERROR": {
|
||||||
|
code: 500,
|
||||||
|
title: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "INTERNAL SERVER ERROR"
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { forwardRef, Module } from '@nestjs/common';
|
import { forwardRef, Module } from '@nestjs/common';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { JwtStrategy } from './jwt.strategy';
|
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { AccountModule } from 'src/modules/account/account.module';
|
import { AccountModule } from 'src/modules/account/account.module';
|
||||||
|
import { JwtAccessStrategy } from './strategy/access-token.strategy';
|
||||||
|
import { JwtRefreshStrategy } from './strategy/refresh-token.strategy';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule,
|
ConfigModule,
|
||||||
@@ -18,7 +18,7 @@ import { AccountModule } from 'src/modules/account/account.module';
|
|||||||
}),
|
}),
|
||||||
forwardRef(() => AccountModule)
|
forwardRef(() => AccountModule)
|
||||||
],
|
],
|
||||||
providers: [AuthService, JwtStrategy],
|
providers: [AuthService, JwtAccessStrategy, JwtRefreshStrategy],
|
||||||
exports: [AuthService]
|
exports: [AuthService]
|
||||||
})
|
})
|
||||||
export class AuthModule{}
|
export class AuthModule{}
|
||||||
@@ -7,7 +7,7 @@ export class AuthService {
|
|||||||
|
|
||||||
generateTokens(payload: any) {
|
generateTokens(payload: any) {
|
||||||
const accessToken = this.jwtService.sign(payload, { expiresIn: '1h' });
|
const accessToken = this.jwtService.sign(payload, { expiresIn: '1h' });
|
||||||
const refreshToken = this.jwtService.sign(payload, { expiresIn: '7d' });
|
const refreshToken = this.jwtService.sign({id: payload.id}, { expiresIn: '7d' });
|
||||||
|
|
||||||
return { accessToken, refreshToken };
|
return { accessToken, refreshToken };
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/middleware/auth/guard/access-token.guard.ts
Normal file
5
src/middleware/auth/guard/access-token.guard.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtAccessAuthGuard extends AuthGuard('access-token') {}
|
||||||
5
src/middleware/auth/guard/refresh-token.guard.ts
Normal file
5
src/middleware/auth/guard/refresh-token.guard.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtRefreshAuthGuard extends AuthGuard('refresh-token') {}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class JwtGuard extends AuthGuard('jwt') {}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { JwtService } from '@nestjs/jwt';
|
|
||||||
import { PassportStrategy } from '@nestjs/passport';
|
|
||||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
|
||||||
import { AccountRepo } from 'src/modules/account/account.repo';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
|
||||||
constructor(
|
|
||||||
private readonly accountRepo: AccountRepo
|
|
||||||
, private readonly configService: ConfigService
|
|
||||||
, private readonly jwtService: JwtService
|
|
||||||
) {
|
|
||||||
super({
|
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
||||||
ignoreExpiration: false,
|
|
||||||
secretOrKey: configService.get<string>('JWT_SECRET')!
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async validate(payload: any) {
|
|
||||||
const account = await this.accountRepo.findById(payload.id);
|
|
||||||
if (!account || account.length < 1) throw new UnauthorizedException();
|
|
||||||
return account[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
src/middleware/auth/strategy/access-token.strategy.ts
Normal file
18
src/middleware/auth/strategy/access-token.strategy.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
|
import { ExtractJwt, Strategy } from "passport-jwt";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtAccessStrategy extends PassportStrategy(Strategy, "access-token") {
|
||||||
|
constructor(configService: ConfigService) {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: configService.get<string>('JWT_SECRET')!
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(payload: any) {
|
||||||
|
return { id: payload.id };
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/middleware/auth/strategy/refresh-token.strategy.ts
Normal file
26
src/middleware/auth/strategy/refresh-token.strategy.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh-token') {
|
||||||
|
constructor(configService: ConfigService) {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: configService.get<string>('JWT_SECRET')!,
|
||||||
|
passReqToCallback: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(req: any, payload: any) {
|
||||||
|
const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);
|
||||||
|
|
||||||
|
if (!token) throw new UnauthorizedException();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: payload.id,
|
||||||
|
token
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +1,60 @@
|
|||||||
import { Body, Controller, Get, Post, Query } from "@nestjs/common";
|
import { Body, Controller, Get, Headers, Post, Query, Req, UseGuards } from "@nestjs/common";
|
||||||
import { AccountService } from "./account.service";
|
import { AccountService } from "./account.service";
|
||||||
import {
|
import * as DTO from "./dto";
|
||||||
CheckDuplicationRequest, CheckDuplicationResponse,
|
import { JwtAccessAuthGuard } from "src/middleware/auth/guard/access-token.guard";
|
||||||
SendVerificationCodeRequest, SendVerificationCodeResponse,
|
import { Public } from "src/common/decorators/public.decorator";
|
||||||
VerifyCodeRequest, VerifyCodeResponse,
|
|
||||||
LoginRequest, LoginResponse,
|
|
||||||
SignupRequest, SignupResponse
|
|
||||||
} from "./dto";
|
|
||||||
|
|
||||||
|
@UseGuards(JwtAccessAuthGuard)
|
||||||
@Controller('account')
|
@Controller('account')
|
||||||
export class AccountController {
|
export class AccountController {
|
||||||
constructor(private readonly accountService: AccountService) {}
|
constructor(private readonly accountService: AccountService) {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Get('/')
|
@Get('/')
|
||||||
async test() {
|
async test() {
|
||||||
return "Test"
|
return "Test"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Get('check-duplication')
|
@Get('check-duplication')
|
||||||
async checkDuplication(@Query() query: CheckDuplicationRequest): Promise<CheckDuplicationResponse> {
|
async checkDuplication(@Query() query: DTO.CheckDuplicationRequest): Promise<DTO.CheckDuplicationResponse> {
|
||||||
return await this.accountService.checkDuplication(query);
|
return await this.accountService.checkDuplication(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Post('send-verification-code')
|
@Post('send-verification-code')
|
||||||
async sendVerificationCode(@Body() body: SendVerificationCodeRequest): Promise<SendVerificationCodeResponse> {
|
async sendVerificationCode(@Body() body: DTO.SendVerificationCodeRequest): Promise<DTO.SendVerificationCodeResponse> {
|
||||||
const result = await this.accountService.sendVerificationCode(body);
|
const result = await this.accountService.sendVerificationCode(body);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Post('verify-code')
|
@Post('verify-code')
|
||||||
async verifyCode(@Body() body: VerifyCodeRequest): Promise<VerifyCodeResponse> {
|
async verifyCode(@Body() body: DTO.VerifyCodeRequest): Promise<DTO.VerifyCodeResponse> {
|
||||||
console.log(body.email);
|
console.log(body.email);
|
||||||
const result = await this.accountService.verifyCode(body);
|
const result = await this.accountService.verifyCode(body);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Post('signup')
|
@Post('signup')
|
||||||
async signup(@Body() body: SignupRequest): Promise<SignupResponse> {
|
async signup(@Body() body: DTO.SignupRequest): Promise<DTO.SignupResponse> {
|
||||||
const result = await this.accountService.signup(body);
|
const result = await this.accountService.signup(body);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
@Post('login')
|
@Post('login')
|
||||||
async login(@Body() body: LoginRequest): Promise<LoginResponse> {
|
async login(@Body() body: DTO.LoginRequest): Promise<DTO.LoginResponse> {
|
||||||
const result = await this.accountService.login(body);
|
const result = await this.accountService.login(body);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Post('refresh-access-token')
|
||||||
|
async refreshAccessToken(@Req() req): Promise<DTO.RefreshAccessTokenResponse> {
|
||||||
|
const id = req.user.id;
|
||||||
|
const newAccessToken = this.accountService.refreshAccessToken(id);
|
||||||
|
return newAccessToken;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -94,10 +94,10 @@ export class AccountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const { id, accountId, name, nickname, email, status, isDeleted, birthday } = queryResult[0];
|
const { id, accountId, status, isDeleted, birthday } = queryResult[0];
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
id, accountId, name, nickname, email, status, isDeleted, birthday
|
id, accountId, status, isDeleted, birthday
|
||||||
};
|
};
|
||||||
|
|
||||||
const { accessToken, refreshToken } = this.authService.generateTokens(payload);
|
const { accessToken, refreshToken } = this.authService.generateTokens(payload);
|
||||||
@@ -109,4 +109,12 @@ export class AccountService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async refreshAccessToken(id: string): Promise<DTO.RefreshAccessTokenResponse> {
|
||||||
|
const { accessToken, refreshToken } = this.authService.refreshTokens(id);
|
||||||
|
return {
|
||||||
|
accessToken: accessToken,
|
||||||
|
refreshToken: refreshToken
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,4 +11,6 @@ export { SignupRequestDto as SignupRequest } from './signup/signup-request.dto';
|
|||||||
export { SignupResponseDto as SignupResponse } from './signup/signup-response.dto';
|
export { SignupResponseDto as SignupResponse } from './signup/signup-response.dto';
|
||||||
|
|
||||||
export { LoginRequestDto as LoginRequest } from './login/login-request.dto';
|
export { LoginRequestDto as LoginRequest } from './login/login-request.dto';
|
||||||
export { LoginResponseDto as LoginResponse } from './login/login-response.dto'
|
export { LoginResponseDto as LoginResponse } from './login/login-response.dto'
|
||||||
|
|
||||||
|
export { RefreshAccessTokenResponseDto as RefreshAccessTokenResponse } from './refreshAccessToken/refresh-access-token-response.dto';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export class RefreshAccessTokenResponseDto {
|
||||||
|
accessToken: string;
|
||||||
|
refreshToken: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user