0

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

15
  • Are you running this app over http or https? secure: true will send cookies only when there is https connection. Please see the quote "A cookie with the Secure attribute is only sent to the server with an encrypted request over the HTTPS protocol. It's never sent with unsecured HTTP (except on localhost),..." citation developer.mozilla.org/en-US/docs/Web/HTTP/Cookies Commented Jun 26 at 7:34
  • Also please try to replace this if (response.status === 200) { navigate('/home'); } with an const response = await axios.get('blog-backend-khj7.onrender.com/home', { withCredentials: true // Include credentials in the request });. This is to test and see if the issue is while invoking axios.get... via the react router. Commented Jun 26 at 7:40
  • @WeDoTheBest4You my application is served over HTTPS, which means the secure attribute for cookies should be set to true. So i think this may not be the problem.I may be wrong. blog-t7q7.onrender.com and i will now try the second solution Thanks for the reply :)
    – elclasico
    Commented Jun 26 at 19:44
  • Nothing is working.Please help.I dont know what to do
    – elclasico
    Commented Jun 27 at 10:58
  • Hi, Have been able to try the second option ? Commented Jun 27 at 11:00

1 Answer 1

0

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.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.