Skip to content

Commit

Permalink
feat/#39/jwt-guard 구현 및 테스트
Browse files Browse the repository at this point in the history
  • Loading branch information
gwgw123 committed Dec 20, 2024
1 parent 41f21cb commit 5eb99b3
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI/CD Docker
# 트리거를 수행할 브랜치를 지정합니다.
on:
push:
branches: [main, develop]
branches: [main, develop, feat/#39/jwt-verification]

# 환경설정
env:
Expand Down
5 changes: 3 additions & 2 deletions src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { ConfigService } from '@nestjs/config';
import { Response } from 'express';
import { AuthService } from './services/auth.service';
import { RedisService } from './redis/redis.service';
import { JwtGuard } from './jwt/jwt.guard';

@Controller('auth')
export class AuthController {
constructor(
private readonly authService: AuthService,
private readonly redisService: RedisService,
private readonly configService: ConfigService,
) {}

Expand Down Expand Up @@ -58,7 +58,8 @@ export class AuthController {
}

@Get('/verify')
@UseGuards(AuthGuard('jwt'))
// @UseGuards(AuthGuard('jwt'))
@UseGuards(JwtGuard)
authTest(@User() user: any) {
return user;
}
Expand Down
3 changes: 2 additions & 1 deletion src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { TokenService } from './services/token.service';
import { JwtStrategy } from './jwt/jwt.strategy';
import { UsersModule } from 'src/users/modules/users.module';
import { RedisModule } from './redis/redis.module';
import { JwtGuard } from './jwt/jwt.guard';

@Module({
imports: [PassportModule, JwtModule, UsersModule, RedisModule],
providers: [AuthService, TokenService, GoogleStrategy, JwtStrategy],
providers: [AuthService, TokenService, GoogleStrategy, JwtStrategy, JwtGuard],
controllers: [AuthController],
})
export class AuthModule {}
105 changes: 105 additions & 0 deletions src/auth/jwt/jwt.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { UsersService } from 'src/users/services/users.service';
import { RedisService } from '../redis/redis.service';
import * as jwt from 'jsonwebtoken';
import { Request } from 'express';

@Injectable()
export class JwtGuard implements CanActivate {
constructor(
private configService: ConfigService,
private usersService: UsersService,
private redisService: RedisService,
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();

const accessToken = request?.cookies?.accessToken;
const refreshToken = request?.cookies?.refreshToken;

try {
// access 토큰이 있는지 검사
if (!accessToken) {
throw new UnauthorizedException('No Access Token provided');
}

console.log(accessToken);

// access 토큰 변조, 만료를 검사
const payload = jwt.verify(
accessToken,
this.configService.get<string>('ACCESS_SECRET'),
) as any;

console.log(payload);

const { userId } = payload;
const user = await this.usersService.getUser(userId);

// 요청객체 user에 user 정보를 담음음
request['user'] = user;

// access 토큰이 잘 있다면 반환가능
return true;
} catch (error) {
// refresh 토큰이 있는지 검사
if (!refreshToken) {
throw new UnauthorizedException('No Refresh Token provided');
}

try {
// refresh 토큰 변조, 만료를 검사
const payload = jwt.verify(
accessToken,
this.configService.get<string>('ACCESS_SECRET'),
) as any;

const { userId } = payload;

// redis에 해당 refresh 토큰 있는지 확인인
const redisRefreshToken = await this.redisService.get(userId);
if (!redisRefreshToken || redisRefreshToken !== refreshToken) {
throw new UnauthorizedException('Invalid Refresh Token');
}

// access 토큰 재발급 및 쿠키에 담기
const newAccessToken = this.NewAccessToken(userId);
this.setAccessTokenCookie(request, newAccessToken);

const user = await this.usersService.getUser(userId);
if (!user) throw new UnauthorizedException('User not found');

request['user'] = user;
return true;
} catch (refreshError) {
throw new UnauthorizedException('Invalid or Expired Refresh Token');
}
}
}
// access 토큰 재발급
private NewAccessToken(userId: string): string {
return jwt.sign(
{ userId },
this.configService.get<string>('ACCESS_SECRET'),
{ expiresIn: this.configService.get<number>('ACCESS_EXPIRATION_TIME') },
);
}

// Access 토큰을 쿠키에 설정
private setAccessTokenCookie(request: Request, accessToken: string): void {
request.res?.cookie('accessToken', accessToken, {
httpOnly: true,
secure: true,
domain: this.configService.get<string>('COOKIE_DOMAIN'),
sameSite: 'none',
maxAge: this.configService.get<number>('COOKIE_EXPIRATION'),
});
}
}
5 changes: 2 additions & 3 deletions src/auth/jwt/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { Request } from 'express';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ExtractJwt, Strategy, VerifiedCallback } from 'passport-jwt';
import * as jwt from 'jsonwebtoken';
import { UsersService } from 'src/users/services/users.service';
import { RedisService } from '../redis/redis.service';
Expand All @@ -26,7 +26,6 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
secretOrKey: configService.get<string>('ACCESS_SECRET'),
// validate 메서드로 request 객체 전달
passReqToCallback: true,
ignoreExpiration: true,
});
}

Expand Down Expand Up @@ -77,7 +76,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
const newAccessToken = this.NewAccessToken(userId);
this.setAccessTokenCookie(request, newAccessToken);

// user 정보가져오기기
// user 정보가져오기
const user = await this.usersService.getUser(userId);
if (!user) throw new UnauthorizedException('User not found');

Expand Down
2 changes: 1 addition & 1 deletion src/auth/redis/redis.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as redisStore from 'cache-manager-redis-store';
store: redisStore,
host: configService.get<string>('REDIS_HOST'),
port: configService.get<number>('REDIS_PORT'),
password: configService.get<string>('REDIS_PASSWORD'),
// password: configService.get<string>('REDIS_PASSWORD'),
ttl: configService.get<number>('REDIS_EXPIRATION_TIME'),
}),
inject: [ConfigService],
Expand Down

0 comments on commit 5eb99b3

Please sign in to comment.