Add Google OAuth authentication infrastructure
- Add passport, passport-google-oauth20, jsonwebtoken, cookie-parser, uuid deps - Create config/auth.js with JWT, cookie, and OAuth configuration - Create models/user.js with MongoDB user model and indexes - Create middleware/passport.js with Google OAuth strategy - Create middleware/auth.js with requireAuth middleware and sliding window refresh - Create routes/auth.js with OAuth flow endpoints - Update server.js to integrate auth, protect all data endpoints (except /health) - Configure CORS for cookie-based authentication
This commit is contained in:
116
routes/auth.js
Normal file
116
routes/auth.js
Normal file
@ -0,0 +1,116 @@
|
||||
const express = require('express');
|
||||
const passport = require('passport');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const authConfig = require('../config/auth');
|
||||
const { requireAuth, generateToken } = require('../middleware/auth');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* GET /auth/google
|
||||
* Initiates Google OAuth flow
|
||||
*/
|
||||
router.get('/google', passport.authenticate('google', {
|
||||
scope: authConfig.google.scope,
|
||||
session: false
|
||||
}));
|
||||
|
||||
/**
|
||||
* GET /auth/google/callback
|
||||
* Handles OAuth callback from Google
|
||||
* On success: generates JWT, sets cookie, redirects to frontend
|
||||
* On failure: redirects to login with error
|
||||
*/
|
||||
router.get('/google/callback',
|
||||
passport.authenticate('google', {
|
||||
session: false,
|
||||
failureRedirect: `${process.env.FRONTEND_URL}/login?error=auth_failed`
|
||||
}),
|
||||
async (req, res) => {
|
||||
try {
|
||||
const user = req.user;
|
||||
|
||||
// Check if email is verified (if available in profile)
|
||||
if (req.authInfo && req.authInfo.emails && req.authInfo.emails[0]) {
|
||||
const emailVerified = req.authInfo.emails[0].verified !== false; // Default to true if not present
|
||||
if (!emailVerified) {
|
||||
return res.redirect(`${process.env.FRONTEND_URL}/login?error=unverified`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user account is active
|
||||
if (!user.isActive) {
|
||||
return res.redirect(`${process.env.FRONTEND_URL}/login`);
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = generateToken(user._id);
|
||||
|
||||
// Set auth cookie
|
||||
res.cookie(authConfig.cookie.name, token, authConfig.cookie.options);
|
||||
|
||||
// Redirect to frontend
|
||||
res.redirect(process.env.FRONTEND_URL);
|
||||
} catch (err) {
|
||||
console.error('OAuth callback error:', err);
|
||||
res.redirect(`${process.env.FRONTEND_URL}/login?error=auth_failed`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* GET /auth/me
|
||||
* Returns current authenticated user's data
|
||||
* Protected route - requires valid JWT
|
||||
*/
|
||||
router.get('/me', requireAuth, (req, res) => {
|
||||
res.json({
|
||||
user: {
|
||||
id: req.user._id,
|
||||
email: req.user.email,
|
||||
name: req.user.name,
|
||||
picture: req.user.picture,
|
||||
role: req.user.role
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /auth/logout
|
||||
* Clears authentication cookie
|
||||
* Protected route - requires valid JWT
|
||||
*/
|
||||
router.post('/logout', requireAuth, (req, res) => {
|
||||
// Clear the auth cookie
|
||||
res.clearCookie(authConfig.cookie.name, {
|
||||
...authConfig.cookie.options,
|
||||
maxAge: 0
|
||||
});
|
||||
|
||||
res.json({ message: 'Logged out successfully' });
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /auth/status
|
||||
* Returns authentication status without requiring middleware
|
||||
* Used for quick frontend checks
|
||||
*/
|
||||
router.get('/status', (req, res) => {
|
||||
const token = req.cookies[authConfig.cookie.name];
|
||||
|
||||
// No token present
|
||||
if (!token) {
|
||||
return res.json({ authenticated: false });
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify the token
|
||||
jwt.verify(token, authConfig.jwt.secret);
|
||||
return res.json({ authenticated: true });
|
||||
} catch (err) {
|
||||
// Token invalid or expired
|
||||
return res.json({ authenticated: false });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user