Reputation: 43
I'm using nodemailer for a registration page and I'm using it with OAuth 2.0 but I made sure everything was set up correctly in the code a hundred times and it didn't work. Anything wrong in my code or I'm doing? Also I made sure the correct redirect uri is used . I can't seem to be able to get around it I've checked all the credentials on oauth website, used a refresh token directly after generating it, but the callback function /google is not being executed and I don't knwo how to add it and to do the whole process.
const express = require('express');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const crypto = require('crypto');
const nodemailer = require('nodemailer');
const { google } = require('googleapis');
const User = require('./models/User');
require('dotenv').config();
const authRouter = express.Router();
// Debug logger
const debug = (message, data = null) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${message}`);
if (data) {
console.log('Data:', JSON.stringify(data, null, 2));
}
};
// Error logger
const logError = (location, error) => {
console.error(`[${new Date().toISOString()}] Error in ${location}:`, {
message: error.message,
stack: error.stack,
details: error
});
};
// Passport configuration
passport.serializeUser((user, done) => {
debug('Serializing user:', { userId: user.id });
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
debug('Deserializing user:', { userId: id });
const user = await User.findById(id);
if (!user) {
debug('User not found during deserialization', { userId: id });
return done(null, null);
}
done(null, user);
} catch (err) {
logError('deserializeUser', err);
done(err, null);
}
});
// Google OAuth Strategy
passport.use(new GoogleStrategy({
clientID: '431154492374-0oi7uhojuiirfsc1e176ibn8mai6pam9.apps.googleusercontent.com',
clientSecret: "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",
callbackURL: "http://localhost:3000/auth/google/callback",
passReqToCallback: true
},
async (req, accessToken, refreshToken, profile, done) => {
try {
debug('Google OAuth callback received', {
profileId: profile.id,
email: profile.emails[0].value,
displayName: profile.displayName
});
// Check if user already exists
const existingUser = await User.findOne({ 'google.id': profile.id });
if (existingUser) {
debug('Existing user found', { userId: existingUser.id });
return done(null, existingUser);
}
debug('Creating new user from Google profile');
// If user doesn't exist, create a new user
const newUser = new User({
name: profile.displayName,
email: profile.emails[0].value,
google: {
id: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
picture: profile.photos[0].value
},
verified: true
});
await newUser.save();
debug('New user created successfully', { userId: newUser.id });
done(null, newUser);
} catch (err) {
logError('GoogleStrategy', err);
done(err, null);
}
}));
// Email transport setup
const createTransporter = async () => {
try {
debug('Creating OAuth2 client for email transport');
const oauth2Client = new google.auth.OAuth2(
'431154492374-0oi7uhojuiirfsc1e176ibn8mai6pam9.apps.googleusercontent.com',
"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",
'http://localhost:3000/auth/google/callback'
);
oauth2Client.setCredentials({
refresh_token: "1//04uhjWsRdK3JxCgYIARAAGAQSNwF-L9Ir7Npj4h_z19y4Nym1C4vZGFCAb9V-39tuP5QJfxqyJRpn2g4pnnv0jhzGyYoXQGSO3Q0"
});
debug('Getting access token');
const accessToken = await oauth2Client.getAccessToken();
debug('Access token received successfully');
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: '[email protected]',
clientId: '431154492374-0oi7uhojuiirfsc1e176ibn8mai6pam9.apps.googleusercontent.com',
clientSecret: "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",
refreshToken: "1//04uhjWsRdK3JxCgYIARAAGAQSNwF-L9Ir7Npj4h_z19y4Nym1C4vZGFCAb9V-39tuP5QJfxqyJRpn2g4pnnv0jhzGyYoXQGSO3Q0",
accessToken: accessToken.token,
},
});
debug('Email transporter created successfully');
return transporter;
} catch (err) {
logError('createTransporter', err);
throw err;
}
};
// Middleware
authRouter.use((req, res, next) => {
debug('Incoming request:', {
method: req.method,
path: req.path,
query: req.query,
body: req.method === 'POST' ? req.body : undefined
});
next();
});
authRouter.use(express.json());
authRouter.use(require('express-session')({
secret: "zA7*@1a$kDq3lH!kJx9pL4#VgB5%hCmU8dF&QpY*^WzXoZsT",
resave: false,
saveUninitialized: true,
}));
authRouter.use(passport.initialize());
authRouter.use(passport.session());
// Google OAuth routes
authRouter.get('/google',
(req, res, next) => {
debug('Starting Google OAuth flow');
next();
},
passport.authenticate('google', {
scope: [
'profile',
'email',
'https://www.googleapis.com/auth/gmail.send'
]
})
);
authRouter.get('/google/callback',
(req, res, next) => {
debug('Received Google OAuth callback', { query: req.query });
next();
},
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
debug('Google authentication successful', { userId: req.user.id });
const token = jwt.sign(
{ id: req.user.id },
'zA7*@1a$kDq3lH!kJx9pL4#VgB5%hCmU8dF&QpY*^WzXoZsT',
{ expiresIn: '24h' }
);
debug('JWT token generated, redirecting to frontend');
res.redirect(`http://localhost:3000/auth/success?token=${token}`);
}
);
// Standard authentication routes
authRouter.post('/register', async (req, res) => {
try {
debug('Registration attempt', { email: req.body.email });
const { name, email, password } = req.body;
if (!name || !email || !password) {
debug('Registration failed - missing fields', {
hasName: !!name,
hasEmail: !!email,
hasPassword: !!password
});
return res.status(400).json({ msg: 'All fields are required.' });
}
const existingUser = await User.findOne({ email });
if (existingUser) {
debug('Registration failed - email exists', { email });
return res.status(400).json({ msg: 'Email already registered.' });
}
const salt = await bcrypt.genSalt(10);
const passwordHash = await bcrypt.hash(password, salt);
const newUser = new User({
name,
email,
password: passwordHash,
verified: false
});
const savedUser = await newUser.save();
debug('New user created', { userId: savedUser.id });
const verificationToken = jwt.sign(
{ id: savedUser._id },
'zA7*@1a$kDq3lH!kJx9pL4#VgB5%hCmU8dF&QpY*^WzXoZsT',
{ expiresIn: '1h' }
);
debug('Sending verification email');
const transporter = await createTransporter();
await transporter.sendMail({
from: '[email protected]',
to: email,
subject: 'Verify your email',
html: `<a href="http://localhost:3000/verify/${verificationToken}">Verify Email</a>`
});
debug('Verification email sent successfully');
res.json({ msg: 'Registration successful. Please check your email to verify your account.' });
} catch (err) {
logError('register', err);
res.status(500).json({ error: err.message });
}
});
authRouter.post('/login', async (req, res) => {
try {
debug('Login attempt', { email: req.body.email });
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
debug('Login failed - user not found', { email });
return res.status(400).json({ msg: 'User not found.' });
}
if (!user.verified) {
debug('Login failed - email not verified', { userId: user.id });
return res.status(400).json({ msg: 'Please verify your email first.' });
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
debug('Login failed - invalid password', { userId: user.id });
return res.status(400).json({ msg: 'Invalid credentials.' });
}
const token = jwt.sign(
{ id: user._id },
'zA7*@1a$kDq3lH!kJx9pL4#VgB5%hCmU8dF&QpY*^WzXoZsT',
{ expiresIn: '24h' }
);
debug('Login successful', { userId: user.id });
res.json({
token,
user: {
id: user._id,
name: user.name,
email: user.email
}
});
} catch (err) {
logError('login', err);
res.status(500).json({ error: err.message });
}
});
module.exports = authRouter;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register, Delete & Get Users</title>
</head>
<body>
<h2>Register</h2>
<form id="registerForm">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required><br><br>
<button type="submit">Register</button>
</form>
<h2>Delete Users</h2>
<!-- Delete All Users Form -->
<form id="deleteAllForm">
<button type="button" id="deleteAllButton">Delete All Users</button>
</form>
<br>
<!-- Delete Specific User by Email Form -->
<form id="deleteUserForm">
<label for="deleteEmail">Delete User by Email:</label>
<input type="email" id="deleteEmail" name="email" required><br><br>
<button type="submit">Delete User</button>
</form>
<h2>Get All Users</h2>
<!-- Button to fetch all users -->
<button id="getAllUsersButton">Get All Users</button>
<!-- Display the list of users -->
<div id="usersList"></div>
<div id="result"></div>
<script>
// Register Form Handling
document.getElementById('registerForm').addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the form from submitting traditionally
// Get form values
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
// Prepare the payload
const data = {
name: name,
email: email,
password: password
};
try {
// Send POST request to register endpoint
const response = await fetch('http://localhost:3000/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
// Parse the response and show result
const result = await response.json();
console.log(result);
if (response.ok) {
document.getElementById('result').innerHTML = `<p style="color: green;">${result.msg}</p>`;
} else {
document.getElementById('result').innerHTML = `<p style="color: red;">${result.msg}</p>`;
}
} catch (error) {
console.error('Error:', error);
document.getElementById('result').innerHTML = `<p style="color: red;">Failed to register. Please try again later.</p>`;
}
});
// Delete All Users Button Handler
document.getElementById('deleteAllButton').addEventListener('click', async function() {
try {
// Send DELETE request to delete all users
const response = await fetch('http://localhost:3000/auth/delete-all', {
method: 'DELETE',
});
const result = await response.json();
if (response.ok) {
document.getElementById('result').innerHTML = `<p style="color: green;">${result.msg}</p>`;
} else {
document.getElementById('result').innerHTML = `<p style="color: red;">${result.msg}</p>`;
}
} catch (error) {
console.error('Error:', error);
document.getElementById('result').innerHTML = `<p style="color: red;">Failed to delete all users. Please try again later.</p>`;
}
});
// Delete Specific User by Email
document.getElementById('deleteUserForm').addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent the form from submitting traditionally
const email = document.getElementById('deleteEmail').value;
try {
// Send DELETE request to delete a user by email
const response = await fetch(`http://localhost:3000/auth/delete/${email}`, {
method: 'DELETE',
});
const result = await response.json();
if (response.ok) {
document.getElementById('result').innerHTML = `<p style="color: green;">${result.msg}</p>`;
} else {
document.getElementById('result').innerHTML = `<p style="color: red;">${result.msg}</p>`;
}
} catch (error) {
console.error('Error:', error);
document.getElementById('result').innerHTML = `<p style="color: red;">Failed to delete user. Please try again later.</p>`;
}
});
// Get All Users Button Handler
document.getElementById('getAllUsersButton').addEventListener('click', async function() {
try {
// Send GET request to get all users
const response = await fetch('http://localhost:3000/auth/users', {
method: 'GET',
});
const result = await response.json();
if (response.ok) {
// Create a list of users
let usersHtml = '<ul>';
result.users.forEach(user => {
usersHtml += `<li>Name: ${user.name}, Email: ${user.email}</li>`;
});
usersHtml += '</ul>';
document.getElementById('usersList').innerHTML = usersHtml;
} else {
document.getElementById('usersList').innerHTML = `<p style="color: red;">${result.msg}</p>`;
}
} catch (error) {
console.error('Error:', error);
document.getElementById('usersList').innerHTML = `<p style="color: red;">Failed to retrieve users. Please try again later.</p>`;
}
});
</script>
</body>
</html>
Upvotes: 1
Views: 118