rup-project/backend/controllers/authController.js

286 lines
7.8 KiB
JavaScript

const { OAuth2Client } = require('google-auth-library');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
require('dotenv').config();
// Google Client ID should be in env, but for now assuming it handles verification
const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
const generateTokens = (user) => {
const payload = { id: user.id, email: user.email, nickname: user.nickname };
const accessToken = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h',
});
const refreshToken = jwt.sign({ id: user.id }, process.env.JWT_REFRESH_SECRET, {
expiresIn: '14d',
});
return { accessToken, refreshToken };
};
// Generic Social Login Logic
const socialLogin = async (provider, socialInfo, res) => {
try {
const { socialId, email, nickname } = socialInfo;
// Find or Create User
const [user, created] = await User.findOrCreate({
where: { provider, socialId },
defaults: { email, nickname },
});
if (!created) {
// Update info if needed (e.g., changed nickname on social side) - Optional
user.email = email || user.email;
user.nickname = nickname || user.nickname;
}
// Generate Tokens
const { accessToken, refreshToken } = generateTokens(user);
// Save Refresh Token to DB
user.refreshToken = refreshToken;
await user.save();
console.log(`[Auth] User ${user.id} logged in via ${provider}`);
return res.status(200).json({
success: true,
accessToken,
refreshToken,
isNewUser: created, // 신규 가입 여부
user: {
id: user.id,
email: user.email,
nickname: user.nickname,
},
});
} catch (error) {
console.error('[Auth Error]', error);
return res.status(500).json({ success: false, message: 'Internal Server Error' });
}
};
// Google Login Handler
// Google Login Handler (Check only, or Login if exists)
exports.loginWithGoogle = async (req, res) => {
const { idToken } = req.body;
if (!idToken) {
return res.status(400).json({ success: false, message: 'Missing idToken' });
}
try {
// Verify Google Token
const ticket = await googleClient.verifyIdToken({
idToken,
audience: [
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_ANDROID_CLIENT_ID
],
});
const payload = ticket.getPayload();
const socialId = payload.sub;
// Check if user exists
const user = await User.findOne({ where: { provider: 'google', socialId } });
if (user) {
// User exists -> Login
const { accessToken, refreshToken } = generateTokens(user);
user.refreshToken = refreshToken;
await user.save();
console.log(`[Auth] User ${user.id} logged in via google`);
return res.status(200).json({
success: true,
accessToken,
refreshToken,
isNewUser: false,
user: {
id: user.id,
email: user.email,
nickname: user.nickname,
},
});
} else {
// User does not exist -> Return isNewUser: true (Do NOT create yet)
return res.status(200).json({
success: true,
isNewUser: true,
// Optional: Return partial info if needed for UI (e.g. pre-filling name)
email: payload.email,
nickname: payload.name,
});
}
} catch (error) {
console.error('[Google Verify Error]', error);
return res.status(401).json({
success: false,
message: 'Invalid Google Token',
debug: error.message
});
}
};
// Google Register Handler (Create User)
exports.registerWithGoogle = async (req, res) => {
const { idToken } = req.body;
if (!idToken) {
return res.status(400).json({ success: false, message: 'Missing idToken' });
}
try {
// Verify Google Token again
const ticket = await googleClient.verifyIdToken({
idToken,
audience: [
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_ANDROID_CLIENT_ID
],
});
const payload = ticket.getPayload();
const socialId = payload.sub;
const email = payload.email;
const nickname = payload.name;
// Find or Create User
const [user, created] = await User.findOrCreate({
where: { provider: 'google', socialId },
defaults: { email, nickname },
});
// If existing user calls register, just log them in (idempotent)
const { accessToken, refreshToken } = generateTokens(user);
user.refreshToken = refreshToken;
await user.save();
console.log(`[Auth] User ${user.id} registered/logged in via google`);
return res.status(200).json({
success: true,
accessToken,
refreshToken,
isNewUser: created,
user: {
id: user.id,
email: user.email,
nickname: user.nickname,
},
});
} catch (error) {
console.error('[Google Register Error]', error);
return res.status(401).json({
success: false,
message: 'Invalid Google Token',
debug: error.message
});
}
};
// Test Login Handler (For verification only)
exports.testLogin = async (req, res) => {
const { email, nickname } = req.body;
if (!email || !nickname) {
return res.status(400).json({ success: false, message: 'Missing email or nickname' });
}
// Use 'google' as provider to satisfy DB Enum constraint
const socialId = `test_${email}`;
return socialLogin('google', { socialId, email, nickname }, res);
};
// Refresh Token Handler
exports.refreshToken = async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(400).json({ success: false, message: 'Refresh Token required' });
}
try {
const secret = process.env.JWT_REFRESH_SECRET;
// 1. Verify Refresh Token
const decoded = jwt.verify(refreshToken, secret);
// 2. Check DB
const user = await User.findByPk(decoded.id);
if (!user || user.refreshToken !== refreshToken) {
return res.status(403).json({ success: false, message: 'Invalid Refresh Token' });
}
// 3. Issue new Access Token
const payload = { id: user.id, email: user.email, nickname: user.nickname };
const newAccessToken = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h',
});
console.log(`[Auth] Access Token refreshed for User ${user.id}`);
return res.status(200).json({
success: true,
accessToken: newAccessToken,
});
} catch (error) {
console.error('[Refresh Error]', error);
if (error.name === 'TokenExpiredError') {
return res.status(403).json({ success: false, message: 'Refresh Token expired' });
}
return res.status(403).json({ success: false, message: 'Invalid Refresh Token' });
}
};
// Get User Info
exports.getMe = async (req, res) => {
try {
// req.user is set by middleware
const user = await User.findByPk(req.user.id);
if (!user) {
return res.status(404).json({ success: false, message: 'User not found' });
}
return res.status(200).json({
success: true,
user: {
id: user.id,
email: user.email,
nickname: user.nickname,
},
});
} catch (error) {
console.error('[GetMe Error]', error);
return res.status(500).json({ success: false, message: 'Internal Server Error' });
}
};
// Withdraw (Delete Account)
exports.withdraw = async (req, res) => {
try {
const userId = req.user.id;
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({ success: false, message: 'User not found' });
}
// Hard Delete
await user.destroy();
console.log(`[Auth] User ${userId} withdrew from the service.`);
return res.status(200).json({ success: true, message: 'Account deleted successfully' });
} catch (error) {
console.error('[Withdraw Error]', error);
return res.status(500).json({ success: false, message: 'Internal Server Error' });
}
};