Search code examples
node.jsexpressaxiospassport.jssession-management

Getting "401 Unauthorized" when accessing protected route in Express.js with express-session and passport


I'm building a blob web application using Express.js where users can log in and access protected routes. I've set up session management using express-session and authentication with passport-local. However, when I try to access the /home route after logging in, I'm consistently getting a "401 Unauthorized" error. It might briefly open for like 1-2 sec and then direct me to /login.

import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import pg from 'pg';
import { fileURLToPath } from 'url';
import path from 'path';
import bcrypt from 'bcrypt';
import passport from 'passport';
import { Strategy } from 'passport-local';
import session from 'express-session';
import dotenv from 'dotenv';

dotenv.config(); 

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

let { PGHOST, PGDATABASE, PGUSER, PGPASSWORD, ENDPOINT_ID } = process.env;

PGPASSWORD = decodeURIComponent(PGPASSWORD);

const pool = new pg.Pool({
  user: PGUSER,
  host: PGHOST,
  database: PGDATABASE,
  password: PGPASSWORD,
  port: 5432,
  ssl: {
    rejectUnauthorized: false,
  },
  connectionTimeoutMillis: 3000, 
});

const app = express();
const PORT = process.env.PORT || 5000;
const saltRounds = 10;

app.use(cors({
    origin: 'https://blog-t7q7.onrender.com',
    credentials: true
}));

app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use(session({
    secret: 'TOPSECRETWORD',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: true, 
        sameSite: 'none', 
        httpOnly: true,
    }
}));

app.use(passport.initialize());
app.use(passport.session());


const isAuthenticated = (req, res, next) => {
  if (res.isAuthenticated()) {
    return next();
  } else {
    res.status(401).json({ error: 'Unauthorized' });
  }
};


app.get("/home", isAuthenticated, async (req, res) => {
    try {
        const result = await pool.query('SELECT post_title, post_content FROM userposts');
        res.status(200).json(result.rows);
    } catch (error) {
        console.error('Error fetching posts:', error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});


app.post("/register", async (req, res) => {
    const { username, userpassword } = req.body;
    try {
        const userCheckResult = await pool.query('SELECT * FROM blog WHERE username = $1', [username]);
        if (userCheckResult.rows.length > 0) {
            return res.status(400).send({ error: "Username already taken" });
        }

        const hashedPassword = await bcrypt.hash(userpassword, saltRounds);
        const result = await pool.query('INSERT INTO blog (username, userpassword) VALUES ($1, $2) RETURNING *', [username, hashedPassword]);
        const user = result.rows[0];
        req.login(user, (err) => {
            if (err) {
                console.log(err);
                res.status(500).send({ error: "Registration failed" });
            } else {
                res.status(200).send({ message: "User registered successfully" });
            }
        });

    } catch (error) {
        console.error("Error inserting user:", error);
        res.status(500).send({ error: "Registration failed" });
    }
});


app.post("/login", (req, res, next) => {
    passport.authenticate("local", (err, user, info) => {
        if (err) {
            console.error('Error during authentication:', err);
            return res.status(500).send({ error: "Internal Server Error" });
        }
        if (!user) {
            console.log('Authentication failed:', info);
            return res.status(401).send({ error: "Invalid username or password" });
        }
        req.logIn(user, (err) => {
            if (err) {
                console.error('Error logging in user:', err);
                return res.status(500).send({ error: "Internal Server Error" });
            }
            return res.status(200).send({ message: "User logged in successfully" });
        });
    })(req, res, next);
});


app.post("/postblog", isAuthenticated, async (req, res) => {
    const { postTitle, postContent } = req.body;
    try {
        await pool.query('INSERT INTO userposts (post_title, post_content) VALUES ($1, $2)', [postTitle, postContent]);
        res.status(200).send({ message: "Post inserted successfully" });
    } catch (error) {
        console.error("Error inserting post:", error);
        res.status(500).send({ error: "Error inserting post" });
    }
});


app.post("/post", isAuthenticated, async (req, res) => {
    const { postTitle } = req.body;
    console.log('Received request to fetch post:', postTitle);
    try {
        const result = await pool.query('SELECT * FROM userposts WHERE post_title = $1', [postTitle]);
        console.log('Query result:', result.rows);
        if (result.rows.length > 0) {
            const post = result.rows[0];
            res.status(200).send({ post });
        } else {
            res.status(404).send({ error: "Post not found" });
        }
    } catch (error) {
        console.error("Error fetching post:", error);
        res.status(500).send({ error: "Error fetching post" });
    }
});

passport.use(new Strategy(
    {
        usernameField: 'username',
        passwordField: 'userpassword'
    },
    async (username, password, cb) => {
        try {
            const result = await pool.query('SELECT * FROM blog WHERE username = $1', [username]);
            if (result.rows.length > 0) {
                const user = result.rows[0];
                const storedHashedPassword = user.userpassword;
                bcrypt.compare(password, storedHashedPassword, (err, isMatch) => {
                    if (err) {
                        return cb(err);
                    }
                    if (isMatch) {
                        return cb(null, user);
                    } else {
                        return cb(null, false, { message: 'Incorrect username or password.' });
                    }
                });
            } else {
                return cb(null, false, { message: 'Incorrect username or password.' });
            }
        } catch (error) {
            console.error("Error logging in user:", error);
            return cb(error);
        }
    }
));

passport.serializeUser((user, cb) => {
    cb(null, user.id);
});

passport.deserializeUser(async (id, cb) => {
    try {
        const result = await pool.query('SELECT * FROM blog WHERE id = $1', [id]);
        if (result.rows.length > 0) {
            cb(null, result.rows[0]);
        } else {
            cb(new Error('User not found'));
        }
    } catch (error) {
        cb(error);
    }
});

app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

this is my index.js

import React, { useState } from "react";
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import './Login.css'; // Import the CSS file

function Login() {
    const [name, setName] = useState("");
    const [password, setPassword] = useState("");
    const navigate = useNavigate();

    const handleNameChange = (event) => {
        setName(event.target.value);
    }

    const handlePasswordChange = (event) => {
        setPassword(event.target.value);
    }

    const loginUser = async (event) => {
        event.preventDefault();
        try {
            const response = await axios.post('https://blog-backend-khj7.onrender.com/login', 
            { username: name, userpassword: password },
            { withCredentials: true }); 

            if (response.status === 200) {
                navigate('/home');
            }
        } catch (error) {
            if (error.response) {
                if (error.response.status === 400 || error.response.status === 401) {
                    alert("Invalid username or password");
                } else {
                    console.error('Error logging in user:', error);
                    alert("There was an error logging in the user");
                }
            } else {
                console.error('Error logging in user:', error);
                alert("There was an error logging in the user");
            }
        }
    }
    
    return (
        <div className="login-background">
            <div className="login-container">
                <h1>Login</h1>
                <form onSubmit={loginUser}>
                    <input type="text" onChange={handleNameChange} name="username" value={name} placeholder="Username" required />
                    <input type="password" onChange={handlePasswordChange} name="userpassword" value={password} placeholder="Password" required />
                    <button type="submit">Submit</button>
                </form>
            </div>
        </div>
    );
}

export default Login;

login.jsx

import React, { useState } from "react";
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import './Register.css'; // Import the CSS file

function Register() {
    const [name, setName] = useState("");
    const [password, setPassword] = useState("");
    const navigate = useNavigate();

    function handleNameChange(event) {
        setName(event.target.value);
    }

    function handlePasswordChange(event) {
        setPassword(event.target.value);
    }

    async function registerUser(event) {
        event.preventDefault();
        try {
            const response = await axios.post('https://blog-backend-khj7.onrender.com/register', 
                { username: name, userpassword: password },
                { withCredentials: true } 
            );
            if (response.status === 200) {
                navigate('/home');
            }
        } catch (error) {
            if (error.response && error.response.status === 400) {
                alert("Username already exists");
            } else {
                console.error('There was an error registering the user:', error);
            }
        }
    }

    function goToLogin() {
        navigate('/login');
    }

    return (
        <div className="register-background">
            <h1 className="register-header">Welcome to blog</h1>
            <div className="register-container">
                <h1>Register</h1>
                <form onSubmit={registerUser}>
                    <input type="text" onChange={handleNameChange} value={name} placeholder="Username" />
                    <input type="password" onChange={handlePasswordChange} value={password} placeholder="Password" />
                    <button type="submit">Submit</button>
                </form>
            </div>
            <h3>Already registered?</h3>
            <h4 onClick={goToLogin} className="blog-go-login">Login</h4>
        </div>
    );
}

export default Register;

register.jsx

The site is live!You can also check https://blog-t7q7.onrender.com/

github https://github.com/AMEYSATI/Blog

The thing is in developer tools when i login correctly the cookie session is passed but i guess it do not get received /home or whatever

Please help me since this is also new to me


Solution

  • Ok so the problem was that before i hosted my project on render it worked i.e.session was succesfully getting created and cokies were getting passed with each request but after it got hosted it cookies were getting passed afer registration and login but the were not gettiing authenticated.

    The solution for this is that you just have to write app.set('trust proxy', 1)

    By setting app.set('trust proxy', 1), you enabled Express to trust the first proxy in the X-Forwarded-* header chain, which is often necessary when your app is behind a proxy like those used by some hosting providers. https://expressjs.com/en/resources/middleware/session.html

    In simple terms its that the user tries to login or request to a particular page that u have authenticated.The request first goes to the proxy server (e.g., a server used by Render, heroku, netlify).The proxy server forwards the request to your Express application.Express does not trust the information from the proxy.It sees the request as coming from the proxy server, not the actual user.Secure cookies and client IP addresses are not handled correctly because Express doesn't trust the proxy.

    So by setting setting app.set('trust proxy', 1) Express trusts the first proxy in the chain.It sees the original user's information (like IP address) provided by the proxy.Secure cookies are handled correctly because Express knows the original connection is secure.