3

I'm trying to implement a login system where an user can register to a website, and then sign in with his account. Once the user is logged in, he can edit his personal information.

To check if the user is logged in, I'm trying to set req.session.isLoggedIn to true and then check if that value is true to access some areas of the website. The thing is that just after I signed in, I print the value of req.session and I see my just setted valued, but after that, when I try to check the value of req.session.isLoggedIn in another route, I get no value.

Here's my code:

const express = require('express');
const app = express();
var { Client } = require('pg');
var bcrypt = require('bcrypt');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var cors = require('cors');
var path = require('path');
var session = require('express-session');
var url = require("url");


app.use(cors());
app.use(express.static(path.join(__dirname, 'client/build')));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 600000000 }}))

const client = new Client({
  user: 'xxxxxxxxxxxxx',
  host: 'xxxxxxxxxxxxx',
  password: 'xxxxxxxxxxxxxxx',
  database: 'xxxxxxxxxxxxxx',
  port: 5432,
  ssl: true
})
client.connect();

/*Rutas*/

/*Seleccionar huellas pertenecientas a una cierta categoria*/
app.get('/api/huellas/:categoria', (req, res) => {
  client.query('SELECT * FROM huellas WHERE categoria = $1 AND activo = TRUE', [req.params.categoria], (err, query) => {
    if (err) {
      console.log(err.stack);
    } else {
      res.json(query.rows);
    }
  });
});

/*Listar todas las huellas*/
app.get('/api/mostrarHuellas', function(req, res, next) {
  client.query('SELECT * FROM huellas', (err, query) => {
    if (err) {
      console.log(err.stack);
    } else {
      res.json(query.rows);
    }
  });
});

app.get('/api/buscarHuellas/', function(req, res) {
  console.log(req);
  console.log("nombre: " + req.query.nombre + " categoria: " + req.query.categoria + " estado: " + req.query.estado);
  client.query('SELECT * FROM huellas WHERE (nombre = $1 AND categoria = $2 AND estado =  $3) AND activo = TRUE', [req.query.nombre, req.query.categoria, req.query.estado], (err, query) => {
    if (err) {
      console.log(err.stack);
    } else {
      res.json(query.rows);
    }
  });
});

app.post("/api/registro", function(req, res) {
  var email = req.body.email;
  var password = bcrypt.hashSync(req.body.password, 10);
  client.query('INSERT INTO usuarios(email, password, huella) VALUES ($1, $2, $3)', [email, password, req.body.huella], function(err, result) {
    if(err) {
      //console.log(err.stack);
      res.json(err);
    }
    else {
      console.log('row inserted');
      res.json("ok");
    }
  });
});

app.post("/api/login", function(req, res) {
  client.query('SELECT * FROM usuarios WHERE email = $1', [req.body.email], (err, query) => {
    if (err) {
      console.log(err.stack);
    } else {
      if(bcrypt.compareSync(req.body.password, query.rows[0].password)){
        req.session.isLoggedIn = true;

        console.log(req.session);
        res.json("ok");
      }
      else{
        res.json("clave invalida");
      }
      res.end();
    }
  });
});

app.get("/api/logout", function(req, res) {
  req.session.destroy();
});

app.get("/api/sessions", function(req, res){
  console.log(req.session);
  if(req.session.isLoggedIn) {
    console.log("logged in!");
  }
});


const port = process.env.PORT || 5000;
app.listen(port);

When I access /api/login/ I receive this output in the terminal, I can see isLoggedIn:

    Session {
  cookie: 
   { path: '/',
     _expires: 2017-09-05T00:29:19.786Z,
     originalMaxAge: 600000000,
     httpOnly: true },
  isLoggedIn: true }

But after that, when I access /api/sessions/ I receive this output:

Session {
  cookie: 
   { path: '/',
     _expires: 2017-09-05T00:29:21.451Z,
     originalMaxAge: 599999999,
     httpOnly: true } }

I'm using Nodejs and Expressjs. Also, I'm serving some static file stored in /client/build, and they are working fine.

Thanks in advance!

EDIT:

Here's what my handle login method looks like, I'm using react and react-router 4:

handleSubmit(event){
   event.preventDefault();
   fetch('/api/login', {
   method: 'post',
   headers: {'Content-Type':'application/json'},
   body: JSON.stringify({
     "email": document.getElementById("email").value,
     "password": document.getElementById("pwd").value
   })
   })
     .then(response => response.json())
     .then(res => {
        switch (res) {
          case "clave invalida":
            alert("clave invalida");
            break;
          case "ok":
            alert("sesion iniciada");
            this.props.history.push("/");
            break;
         default:
           alert("Error. Contacte a un administrador");
           break;
       }
     })
     .catch(err => console.log(err));
  };
Julio
  • 407
  • 1
  • 4
  • 14

2 Answers2

5

Well, I just found a solution for my problem. I used the solution posted by @ytibrewala here and the comment made by @nlawson here. This is what I did:

Apparently, by default, fetch method doesn't send cookies, so you need to set the credentials parameter inside the AJAX call, I did it this way:

AJAX call

  handleSubmit(event){
   event.preventDefault();
   fetch('http://localhost:5000/api/login', {
   method: 'post',
   credentials: 'include',
   headers: {'Content-Type':'application/json'},
   body: JSON.stringify({
     "email": document.getElementById("email").value,
     "password": document.getElementById("pwd").value
   })
   })
     .then(response => response.json())
     .then(res => {
       console.log(res);
       if(res.isLoggedIn){
         alert("Signed in");
         this.props.history.push("/hueprint");
       }
       else{
         alert("Invalid user or password");
       }
     })
     .catch(err => console.log(err));
  };

I used include because I'm not working with the same origin. More information about the values that the credentials parameter accepts can be found here

Then, I was facing a CORS issue in my browser, so I changed this on my index.js file on my back end:

index.js

app.use(cors({credentials: true, origin: true}));

Now, everytime I use my handleSubmit method in my website, and I checked the test route that prints req.session I see my isLoggedIn parameter properly setted.

I leave my route, for those who want to see it:

app.post("/api/login", function(req, res) {
  client.query('SELECT * FROM usuarios WHERE email = $1', [req.body.email], (err, query) => {
    if (err) {
      console.log(err.stack);
    }
    else {
      if(bcrypt.compareSync(req.body.password, query.rows[0].password)){
        console.log("password matches");
        req.session.isLoggedIn = true;
        req.session.save();
        res.send(req.session);
      }
      else{
        console.log("password doesn't match");
        req.session.isLoggedIn = false;
        req.session.save();
        res.send(req.session);
      }
    }
  });
});
Julio
  • 407
  • 1
  • 4
  • 14
  • 1
    well, wow, I've learned new stuff from you too, thanks for this great response! Checking https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials this page now, credentials have to be `true` for the page to use the data :) nice work, – Ahmed Can Unbay Aug 29 '17 at 18:02
  • 1
    Thank you. I was hunting around for this answer all day. Using `credentials: 'include'` with `fetch()` requests and adding `{credentials: true, origin: true}` to the `cors()` call options solved request.session data being `undefined`. – Chad Johnson Dec 12 '17 at 05:16
1

You need to send the cookies with the res object whenever you want to save them. Here is my code and it works. Check it out.

app.use(session({
  secret: 'keyboard cat',
  resave: true,
  saveUninitialized: true,
}))    


app.get("/", function(req, res){
  if(req.session.views){
    req.session.views++;
  }else{
    req.session.views = 1;
  }
  res.status(200).send(req.session);
})


app.get("/checkerPage", function(req, res){
  console.log(req.session); //it logs the correct data.
  res.send("whatever");
})

//post req

app.post("/post", function(req, res){
  req.session.user = "myname";
  res.send(req.session);
  console.log(req.session);
});

my index html

<form action="/post" method="post">
  <input type="text" name="myName" value="">
  <input type="submit" name="" value="submit">
</form>
Ahmed Can Unbay
  • 2,694
  • 2
  • 16
  • 33
  • I tried your solution, with a GET and a POST routes, but only got it to work with the GET route. Do you have any clue why this might be happening? The res.status(200).send(req.session) is inside the conditional block, it shouldn't be giving async/problems or something like that. I posted my methods, and the outputs I'm receiving [here](https://pastebin.com/3N73huuC) – Julio Aug 29 '17 at 05:22
  • are you sure? it works for me. can you check the update? @Julio – Ahmed Can Unbay Aug 29 '17 at 05:28
  • maybe try to remove `status(200)` – Ahmed Can Unbay Aug 29 '17 at 05:29
  • I've been trying different things for a while, and it only works if i use the `action="link/to/the/api"` in the form. But when I use my AJAX call in the onSubmit event, it doesn't work, req.session.isLoggedIn is not stored. [Here](https://pastebin.com/n6VCkjjF) is my React component with the handleSubmit method. The thing with using directly action="link-to-api" is that I receive a blank screen with the cookie information, I cannot handle that (or at least, I don't know how to do it the way I did in the pastebin I'm leaving here). Thanks in advance @turmuka! – Julio Aug 29 '17 at 16:30
  • I just solved my problem, I posted the solution. Anyway, I want to thank you for taking the time of helping me with my issue, I learned a couple of new things with your answer. – Julio Aug 29 '17 at 17:51