196 lines
4.4 KiB
JavaScript
196 lines
4.4 KiB
JavaScript
// src/controllers/auth.controller.js
|
|
// 인증 컨트롤러
|
|
|
|
const jwt = require('jsonwebtoken');
|
|
const { User } = require('../models');
|
|
const logger = require('../config/logger.config');
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
|
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
|
|
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret';
|
|
const JWT_REFRESH_EXPIRES_IN = process.env.JWT_REFRESH_EXPIRES_IN || '30d';
|
|
|
|
/**
|
|
* JWT 토큰 생성
|
|
*/
|
|
function generateTokens(user) {
|
|
const accessToken = jwt.sign(
|
|
{ userId: user.id, email: user.email, role: user.role },
|
|
JWT_SECRET,
|
|
{ expiresIn: JWT_EXPIRES_IN }
|
|
);
|
|
|
|
const refreshToken = jwt.sign(
|
|
{ userId: user.id },
|
|
JWT_REFRESH_SECRET,
|
|
{ expiresIn: JWT_REFRESH_EXPIRES_IN }
|
|
);
|
|
|
|
return { accessToken, refreshToken };
|
|
}
|
|
|
|
/**
|
|
* 회원가입
|
|
*/
|
|
exports.register = async (req, res, next) => {
|
|
try {
|
|
const { email, password, name } = req.body;
|
|
|
|
// 이메일 중복 확인
|
|
const existingUser = await User.findOne({ where: { email } });
|
|
if (existingUser) {
|
|
return res.status(409).json({
|
|
success: false,
|
|
error: {
|
|
code: 'EMAIL_ALREADY_EXISTS',
|
|
message: '이미 등록된 이메일입니다.',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 사용자 생성
|
|
const user = await User.create({
|
|
email,
|
|
password,
|
|
name,
|
|
});
|
|
|
|
// 토큰 생성
|
|
const tokens = generateTokens(user);
|
|
|
|
logger.info(`새 사용자 가입: ${email}`);
|
|
|
|
return res.status(201).json({
|
|
success: true,
|
|
data: {
|
|
user: user.toSafeJSON(),
|
|
...tokens,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 로그인
|
|
*/
|
|
exports.login = async (req, res, next) => {
|
|
try {
|
|
const { email, password } = req.body;
|
|
|
|
// 사용자 조회
|
|
const user = await User.findOne({ where: { email } });
|
|
if (!user) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: 'INVALID_CREDENTIALS',
|
|
message: '이메일 또는 비밀번호가 올바르지 않습니다.',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 비밀번호 검증
|
|
const isValidPassword = await user.validatePassword(password);
|
|
if (!isValidPassword) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: 'INVALID_CREDENTIALS',
|
|
message: '이메일 또는 비밀번호가 올바르지 않습니다.',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 계정 상태 확인
|
|
if (user.status !== 'active') {
|
|
return res.status(403).json({
|
|
success: false,
|
|
error: {
|
|
code: 'ACCOUNT_INACTIVE',
|
|
message: '계정이 비활성화되었습니다. 관리자에게 문의하세요.',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 마지막 로그인 시간 업데이트
|
|
user.lastLoginAt = new Date();
|
|
await user.save();
|
|
|
|
// 토큰 생성
|
|
const tokens = generateTokens(user);
|
|
|
|
logger.info(`사용자 로그인: ${email}`);
|
|
|
|
return res.json({
|
|
success: true,
|
|
data: {
|
|
user: user.toSafeJSON(),
|
|
...tokens,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 토큰 갱신
|
|
*/
|
|
exports.refresh = async (req, res, next) => {
|
|
try {
|
|
const { refreshToken } = req.body;
|
|
|
|
// 리프레시 토큰 검증
|
|
let decoded;
|
|
try {
|
|
decoded = jwt.verify(refreshToken, JWT_REFRESH_SECRET);
|
|
} catch (error) {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: 'INVALID_REFRESH_TOKEN',
|
|
message: '유효하지 않은 리프레시 토큰입니다.',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 사용자 조회
|
|
const user = await User.findByPk(decoded.userId);
|
|
if (!user || user.status !== 'active') {
|
|
return res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: 'USER_NOT_FOUND',
|
|
message: '사용자를 찾을 수 없습니다.',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 새 토큰 생성
|
|
const tokens = generateTokens(user);
|
|
|
|
return res.json({
|
|
success: true,
|
|
data: tokens,
|
|
});
|
|
} catch (error) {
|
|
return next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 로그아웃
|
|
*/
|
|
exports.logout = async (req, res) => {
|
|
// 클라이언트에서 토큰 삭제 처리
|
|
// 서버에서는 특별한 처리 없음 (필요시 블랙리스트 구현)
|
|
return res.json({
|
|
success: true,
|
|
data: {
|
|
message: '로그아웃되었습니다.',
|
|
},
|
|
});
|
|
};
|