Nest.js-auth-jwt (04)

김세겸·2023년 1월 9일
0

NestJS

목록 보기
3/18

1. refresh-token

리프레시 토큰이란? access-token이 탈취를 당했을 경우 보안에 취약한 점을 보완 하기 위해 존재.

2. 적용

cookie에 refresh-token을 저장 하기 위해 설치 할 프로그램
$ npm install --save cookie-parser
$ npm install --save-dev @types/cookie-parser
// main.ts에 적용
import * as cookieParser from 'cookie-parser';
// somewhere in your initialization file
app.use(cookieParser());
//service 수정
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import * as bcrypt from "bcrypt";
import { CreateUserDto } from "./dto/create-user.dto";
import { User } from "./user.entity";
import { LoginUserDto } from "./dto/login-uiser.dto";
import { JwtService } from "@nestjs/jwt";
import { Response } from "express";

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(User)
        private readonly userRepository: Repository<User>,
        private readonly jwtService: JwtService
    ){}

    async create(createUserDto: CreateUserDto):Promise<User> {
        const {email, password} = createUserDto;
        const hashedPassword = await bcrypt.hash(password, 10);
        const result = await this.userRepository.save({
            email,
            password: hashedPassword
        });
        return result
    }

    async login(loginUserDto: LoginUserDto, res: Response) {
        const {email, password} = loginUserDto;
        const user = await this.findOneUser(email);
        
        if(!user) throw new UnauthorizedException("이메일과 비밀번호를 확인해 주세요.");

        const isPasswordCompare = await bcrypt.compare(password, user.password);

        if(!isPasswordCompare) throw new UnauthorizedException("이메일과 비밀번호를 확인해 주세요");
        this.setRefreshToken(user, res);
        
        return this.getAccessToken(user);
    }

    private setRefreshToken(user: User, res: Response) {
        const payload = { email: user.email, sub: user.id };
        const refresh_token =  this.jwtService.sign(
            {payload},
            {secret: 'secret', expiresIn: '1h'}
        );      
        res.cookie('refresh_token', refresh_token, {
            httpOnly: true,
            maxAge: 30 * 24 * 60 * 60 * 1000 //30 day
        });
    }

    private getAccessToken(user: User) {
        const payload = { email: user.email, sub: user.id };
        const access_token =  this.jwtService.sign(
            {payload},
            {secret: 'secret', expiresIn: '1h'}
        )
        return access_token
    }

    async findOneUser(email: string): Promise<User> | null {
        return await this.userRepository.findOne({where: {email}, select: {email:true, id:true}});
    }

    findMe(): string {
        return "qqqq"
    }
}
// 로그인 할경우 access_token을 리턴해 주고 refresh_token은 cookie에 저장 한다.

쿠키에 정상적으로 저장 되고 있다.

3. restore-token

로그인시 access-token과 refresh-token모두 발급해 주었다. 이제 refresh토큰을 가지고 있을경우 access-token을 재발급 해주는 로직을 짜보자
// 우선 받아오는 값을 확인해 보자

// refresh-token strategy
export class LocalRefreshToken extends PassportStrategy(Strategy, "local-refresh-jwt") {
    constructor() {
        super({
            jwtFromRequest: (req: Request) => {
                console.log(req.headers.cookie);
                
            },
            secretOrKey: "secret",
        })
    }

    async validate(payload) {
        return payload;
    }
}

// user.module 추가
import { Module } from "@nestjs/common";
import { JwtModule } from "@nestjs/jwt";
import { TypeOrmModule } from "@nestjs/typeorm";
import { LocalAccessToken, LocalRefreshToken } from "src/commons/auth/jwt-local.strategy";
import { UserController } from "./user.controller";
import { User } from "./user.entity";
import { UserService } from "./user.service";

@Module({
    imports: [
        TypeOrmModule.forFeature([User]),
        JwtModule.register({}),
    ],
    providers: [
        UserService,
        LocalAccessToken,
        LocalRefreshToken
    ],
    controllers: [
        UserController,
    ]
})
export class UserModule {}

// controller에 refresh-token전략 추가
 @UseGuards(AuthGuard("local-refresh-jwt"))
    @Get('restore')
    async resotreToken() {
        return 'asdfasdfasdf'
    }

파싱 해서 validate로 던져주자
export class LocalRefreshToken extends PassportStrategy(Strategy, "local-refresh-jwt") {
    constructor() {
        super({
            jwtFromRequest: (req: Request) => {
                return req.cookies['refresh_token']
            },
            secretOrKey: "secret",
        })
    }

    async validate(payload) {        
        return payload;
    }
}

// strategy로 예외처리를 해줬으므로, restore경로 추가하기
@UseGuards(AuthGuard("local-refresh-jwt"))
@Get('restore')
async resotreToken(@Req() req) {
  return await this.userService.getAccessToken(req.user);
}

0개의 댓글