혼자서 다시 로그인에 대해서 복기해보면서 스터디를 하고자
NestJS로 인증 시스템을 구현하며 accessToken과 refreshToken을 사용하는 전형적인 JWT 인증 구조를 정리해봤다.
service의 구조영역은 따로표현하지 않고 controller기준까지로만 해당 내용을 작성해보았다.
accessToken: 15분 유효 / Authorization 헤더에 저장refreshToken: 7일 유효 / HttpOnly 쿠키에 저장accessToken 만료 시 → refreshToken으로 /auth/refresh API 호출refreshToken 쿠키 제거 (clearCookie)POST /auth/logincontroller에서 아래와 같이 처리하였다.
return의 user 부분은 프론트에서 유저의 정보를 사용한다는 가정에서 추가한 값이다.
@Post('login')
async login(
@Body() loginDto: LoginDTO,
@Res({ passthrough: true }) res: Response,
) {
const loginInfo = await this.authService.login(loginDto);
res.header('Access-Control-Expose-Headers', 'Authorization');
res.setHeader('Authorization', 'Bearer ' + loginInfo.accessToken);
res.cookie('refreshToken', loginInfo.refreshToken, {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7, // 7일
});
return {
message: 'success',
user: loginInfo.payload,
};
}
POST /auth/refresh@Post('refresh')
async refresh(@Req() req: Request, @Res({ passthrough: true }) res: Response) {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) throw new UnauthorizedException('Refresh token not found');
const { accessToken, refreshToken: newRefreshToken } =
await this.authService.refreshAccessToken(refreshToken);
res.setHeader('Authorization', 'Bearer ' + accessToken);
res.cookie('refreshToken', newRefreshToken, {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7,
});
}
GET /auth/me@Get('me')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth('accessToken')
getMe(@Req() req: Request) {
return req.user;
}
이번에는 스웨거를 사용였다.
Swagger에서 테스트 시Authorize버튼 클릭 →Bearer <accessToken>입력 필요
POST /auth/logout이번의 케이스는 accessToken은 reqeust의 headers의 Authorization에 있기때문에 해당 부분은 클라이언트(프론트)에서 핸들링 하여야 한다
그래서 로그아웃 경우에는 refreshToken의 쿠키만 제거해주는것이 api에서의 역할이다.@Post('logout') logout(@Res({ passthrough: true }) res: Response): ResponseCommonSuccessDTO { res.clearCookie('refreshToken'); return { message: 'logout', statusCode: HttpStatus.OK, }; }
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET_KEY || 'jwtSecretKey',
ignoreExpiration: false,
});
}
async validate(payload: Payload) {
return {
id: payload.id,
userUuid: payload.userUuid,
name: payload.name,
email: payload.email,
};
}
}
.addBearerAuth(
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
name: 'Authorization',
in: 'header',
},
'accessToken',
)
@ApiBearerAuth('accessToken')를 사용하는 API에서 Swagger UI의 Authorize 버튼으로 토큰 입력 가능
| 항목 | 저장 위치 | 설명 |
|---|---|---|
accessToken | Authorization 헤더 | 클라이언트에서 인증 요청 시 사용 |
refreshToken | HttpOnly 쿠키 | 토큰 만료 시 재발급 용도 |
getMe | JWT 유효성 검사 | JwtAuthGuard를 통해 검증 |
logout | 쿠키 제거 | 서버 측에서 refreshToken 쿠키 삭제 |
RoleGuard