georgio bou sleimen
georgio bou sleimen

Reputation: 43

Why I'm always getting 'Unauthorized access' on oauth2 authorization despite correct client_id, client_secret, refresh_token (which didn't expire)?

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

Answers (0)

Related Questions