Skip to content

Commit

Permalink
feat: refresh token 인증 과정에서 db 참조하게 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
summersummerwhy committed Dec 2, 2024
1 parent a8f740a commit 65d00bc
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 37 deletions.
20 changes: 8 additions & 12 deletions apps/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ export class AuthController {
// 네이버 인증 후 사용자 정보 반환
const user = req.user;

// primary Key인 id 포함 payload 생성, access token 만들기
const payload = { sub: user.id };
const accessToken = this.tokenService.generateAccessToken(payload);
// access token 만들기
const accessToken = this.tokenService.generateAccessToken(user.id);

// access token 만들어서 db에도 저장
const refreshToken = this.tokenService.generateRefreshToken();
this.authService.updateRefreshToken(user.id, refreshToken);
// refresh token 만들어서 db에도 저장
const refreshToken = await this.tokenService.generateRefreshToken(user.id);

// 토큰을 쿠키에 담아서 메인 페이지로 리디렉션
this.tokenService.setAccessTokenCookie(res, accessToken);
Expand All @@ -69,13 +67,11 @@ export class AuthController {
/// 카카오 인증 후 사용자 정보 반환
const user = req.user;

// primary Key인 id 포함 payload 생성, access token 만들기
const payload = { sub: user.id };
const accessToken = this.tokenService.generateAccessToken(payload);
// access token 만들기
const accessToken = this.tokenService.generateAccessToken(user.id);

// access token 만들어서 db에도 저장
const refreshToken = this.tokenService.generateRefreshToken();
this.authService.updateRefreshToken(user.id, refreshToken);
// refresh token 만들어서 db에도 저장
const refreshToken = await this.tokenService.generateRefreshToken(user.id);

// 토큰을 쿠키에 담아서 메인 페이지로 리디렉션
this.tokenService.setAccessTokenCookie(res, accessToken);
Expand Down
14 changes: 0 additions & 14 deletions apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,4 @@ export class AuthService {
// DB에 있는 값과 일치하는지 비교한다
return user.refreshToken === refreshToken;
}

async updateRefreshToken(id: number, refreshToken: string) {
// 유저를 찾는다.
const user = await this.userRepository.findOneBy({ id });

// 유저가 없으면 오류
if (!user) {
throw new UserNotFoundException();
}

// 유저의 현재 REFRESH TOKEN 갱신
user.refreshToken = refreshToken;
await this.userRepository.save(user);
}
}
2 changes: 2 additions & 0 deletions apps/backend/src/auth/token/token.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { TokenService } from './token.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { UserModule } from '../../user/user.module';

@Module({
imports: [
UserModule,
ConfigModule, // ConfigModule 등록
JwtModule.registerAsync({
imports: [ConfigModule], // ConfigModule에서 환경 변수 로드
Expand Down
68 changes: 57 additions & 11 deletions apps/backend/src/auth/token/token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Response } from 'express';
import { v4 as uuidv4 } from 'uuid';
import { UserRepository } from '../../user/user.repository';
import { UserNotFoundException } from '../../exception/user.exception';
import { InvalidTokenException } from '../../exception/invalid.exception';

const HOUR = 60 * 60;
const DAY = 24 * 60 * 60;
Expand All @@ -10,21 +13,27 @@ const MS_HALF_YEAR = 6 * 30 * 24 * 60 * 60 * 1000;

@Injectable()
export class TokenService {
constructor(private readonly jwtService: JwtService) {}
constructor(
private readonly jwtService: JwtService,
private readonly userRepository: UserRepository,
) {}

generateAccessToken(payload: any): string {
generateAccessToken(userId: number): string {
const payload = { sub: userId };
return this.jwtService.sign(payload, {
expiresIn: HOUR,
});
}

generateRefreshToken(): string {
const payload = {
jti: uuidv4(),
};
return this.jwtService.sign(payload, {
async generateRefreshToken(userId: number): Promise<string> {
const payload = { sub: userId, jti: uuidv4() };
const refreshToken = this.jwtService.sign(payload, {
expiresIn: FIVE_MONTHS,
});

await this.updateRefreshToken(userId, refreshToken);

return refreshToken;
}

generateInviteToken(workspaceId: number, role: string): string {
Expand All @@ -42,16 +51,23 @@ export class TokenService {
});
}

// 후에 DB 로직 (지금은 refreshToken이 DB로 관리 X)
// 추가될 때를 위해 일단 비동기 선언
async refreshAccessToken(refreshToken: string): Promise<string> {
// refreshToken을 검증한다
// refreshToken 1차 검증한다
const decoded = this.jwtService.verify(refreshToken, {
secret: process.env.JWT_SECRET,
});

// 검증된 토큰에서 사용자 ID 추출
const userId = decoded.sub;

// DB에 저장된 refreshToken과 비교
const isValid = await this.compareStoredRefreshToken(userId, refreshToken);
if (!isValid) {
throw new InvalidTokenException();
}

// 새로운 accessToken을 발급한다
return this.generateAccessToken({ sub: decoded.sub });
return this.generateAccessToken(decoded.sub);
}

setAccessTokenCookie(response: Response, accessToken: string): void {
Expand Down Expand Up @@ -86,4 +102,34 @@ export class TokenService {
sameSite: 'strict',
});
}

private async compareStoredRefreshToken(
id: number,
refreshToken: string,
): Promise<boolean> {
// 유저를 찾는다.
const user = await this.userRepository.findOneBy({ id });

// 유저가 없으면 오류
if (!user) {
throw new UserNotFoundException();
}

// DB에 있는 값과 일치하는지 비교한다
return user.refreshToken === refreshToken;
}

private async updateRefreshToken(id: number, refreshToken: string) {
// 유저를 찾는다.
const user = await this.userRepository.findOneBy({ id });

// 유저가 없으면 오류
if (!user) {
throw new UserNotFoundException();
}

// 유저의 현재 REFRESH TOKEN 갱신
user.refreshToken = refreshToken;
await this.userRepository.save(user);
}
}

0 comments on commit 65d00bc

Please sign in to comment.