require-access-token.middleware.js
import jwt from 'jsonwebtoken';
import { HTTP_STATUS } from '../constants/http-status.constant.js';
import { MESSAGES } from '../constants/message.constant.js';
import { ACCESS_TOKEN_SECRET } from '../constants/env.constant.js';
import { prisma } from '../utils/prisma.util.js';
export const requireAccessToken = async (req, res, next) => {
try {
// 인증 정보 파싱
const authorization = req.headers.authorization;
// Authorization이 없는 경우
if (!authorization) {
return res.status(HTTP_STATUS.UNAUTHORIZED).json({
status: HTTP_STATUS.UNAUTHORIZED,
message: MESSAGES.AUTH.COMMON.JWT.NO_TOKEN,
});
}
// JWT 표준 인증 형태와 일치하지 않는 경우
const [type, accessToken] = authorization.split(' ');
if (type !== 'Bearer') {
return res.status(HTTP_STATUS.UNAUTHORIZED).json({
status: HTTP_STATUS.UNAUTHORIZED,
message: MESSAGES.AUTH.COMMON.JWT.NOT_SUPPORTED_TYPE,
});
}
// AccessToken이 없는 경우
if (!accessToken) {
return res.status(HTTP_STATUS.UNAUTHORIZED).json({
status: HTTP_STATUS.UNAUTHORIZED,
message: MESSAGES.AUTH.COMMON.JWT.NO_TOKEN,
});
}
let payload;
try {
payload = jwt.verify(accessToken, ACCESS_TOKEN_SECRET);
} catch (error) {
// AccessToken의 유효기한이 지난 경우
if (error.name === 'TokenExpiredError') {
return res.status(HTTP_STATUS.UNAUTHORIZED).json({
status: HTTP_STATUS.UNAUTHORIZED,
message: MESSAGES.AUTH.COMMON.JWT.EXPIRED,
});
}
// 그 밖의 AccessToken 검증에 실패한 경우
else {
return res.status(HTTP_STATUS.UNAUTHORIZED).json({
status: HTTP_STATUS.UNAUTHORIZED,
message: MESSAGES.AUTH.COMMON.JWT.INVALID,
});
}
}
// Payload에 담긴 사용자 ID와 일치하는 사용자가 없는 경우
const { id } = payload;
const user = await prisma.user.findUnique({
where: { id },
omit: { password: true },
});
if (!user) {
return res.status(HTTP_STATUS.UNAUTHORIZED).json({
status: HTTP_STATUS.UNAUTHORIZED,
message: MESSAGES.AUTH.COMMON.JWT.NO_USER,
});
}
req.user = user;
next();
} catch (error) {
next(error);
}
};
requireAccessToken 미들웨어의 역할:
Authorization 헤더에 포함된 JSON Web Token(JWT)을 검증하여 사용자를 인증합니다. 인증에 성공하면 사용자 정보를 req 객체에 추가하여 이후의 라우트 핸들러에서 접근할 수 있도록 합니다.코드 단계별 설명:
Authorization 헤더를 추출하고, 토큰이 존재하는지 확인합니다. 토큰이 없으면 401 Unauthorized 상태 코드와 함께 에러 응답을 반환합니다.401 Unauthorized 응답을 반환합니다.jsonwebtoken 라이브러리를 사용해 토큰의 서명을 ACCESS_TOKEN_SECRET으로 검증합니다. 만료되었거나 유효하지 않은 토큰은 각각 적절한 에러 메시지와 함께 처리됩니다.401 Unauthorized 응답을 반환합니다.req.user에 추가하여 이후 미들웨어나 라우트 핸들러에서 사용할 수 있도록 합니다.처리된 주요 에러 시나리오:
Authorization 헤더가 없거나 형식이 잘못된 경우.TokenExpiredError).에러 처리:
보안 고려 사항:
ACCESS_TOKEN_SECRET은 환경 변수나 안전한 설정 관리 도구를 사용해 안전하게 저장해야 하며, 코드에 하드코딩하지 않아야 합니다.password 필드를 제외하여 민감한 데이터 노출을 최소화합니다.Prisma와의 통합:
prisma.user.findUnique 메서드를 사용해 사용자 ID로 데이터를 조회하며, 데이터베이스와의 긴밀한 통합을 통해 사용자 검증을 수행합니다.이 미들웨어는 API 보안을 위한 기초 레이어로, 인증과 에러 처리에 있어 모범 사례를 보여줍니다.