Intro to Express Sessions and Bcrypt




Lesson Objectives

  1. Explain what is Bcrypt
  2. Explain why we need environmental variables
  3. Explain what a session is
  4. Use express-session package as middleware
  5. Save user information on the session object
  6. Retrieve user information saved on the session object
  7. Update user information saved on the session object
  8. Destroy the session



Explain what a session is

Cookies are little strings of data that get stored on your computer so that, when you return to a web page, it will remember what you did the last time you were there. You can specify how long a cookie will stay around on a browser before it "expires" or is deleted. This can be a specific date, or it can end as soon as the user closes their browser.


The problem with cookies is that if you store sensitive information in them (usernames, etc), someone could take the computer and view this sensitive information just by opening up the web browser. Sessions are basically cookies, but the server stores the sensitive info in its own memory and passes an encrypted string to the browser, which gets stored in the cookie. The server then uses this encrypted string to know what was saved on the user's computer.


Sessions typically only last for as long as the user keeps their window open, and aren't assigned a specific date to expire. BE CAREFUL: IF YOU RESTART YOUR SERVER, IT WILL LOSE ALL MEMORY OF THE SESSIONS IT CREATED, AND USERS' SESSIONS WILL NOT WORK




Set up environmental variables

We need a way to protect our sensitive information and a way to store environmental variables that are specific to our computer (in contrast to a co-workers computer or the environment in a cloud service).


Typically we'll have a .gitignore file to help with this. Sometimes this is a global file, sometimes we add is per-project. This file tells git which files to ignore when tracking our files. In there it states to never track node_modules nor .env - that way our values stay safely on our machines.


Before we dive into adding express sessions to an app, let's just get some practice with them:


  • mkdir auth-sessions-demo
  • touch server.js
  • npm init -y
  • npm install express express-session bcrypt dotenv
  • touch .env



Set up ENV Variables

In .env:

PORT=3000
SECRET=feedmeseymour

We'll be using this SECRET value soon. In general, it should be a completely random string. You do not want to copy this value from app to app or your stuff can get hacked. Feel free to jazz up your secret now if you like!




Set up Basic Express Server

In server.js:

// Dependencies
const express = require('express');
const app = express();
require('dotenv').config();

// Listener
const PORT = process.env.PORT;
app.listen(PORT, () => console.log(`server is listening on port: ${PORT}`));



Configure express-session Middleware

In server.js:

// Dependencies 
const session = require('express-session');


// Middleware 
app.use(
    session({
        secret: process.env.SECRET,
        resave: false,
        saveUninitialized: false
    })
);

More info on the default resave value: https://www.npmjs.com/package/express-session#resave

More info on the default saveUninitialized value: https://www.npmjs.com/package/express-session#resave




Save user information on the session object

For each of the routes you create, the req variable will now have a session property which is itself an object. You can put things on this.

In server.js:

// Routes / Controllers
app.get('/any', (req, res) => {
    req.session.anyProperty = 'any value';
    res.send('This is the route that sets the value of req.session.anyProperty');
});



Retrieve user information saved on the session object

Once you add a property onto the session object, you can retrieve it when a user navigates to any other route. Then you can use it to make decisions based on the design of your application. Remember though, this session will end when the user closes their browser, or you restart your server app.

// Routes / Controllers
app.get('/retrieve', (req, res) => {
    if (req.session.anyProperty === 'something you want it to') {
        //test to see if that value exists
        //do something if it's a match
        res.send('it matches! cool');
    } else {
        //do something else if it's not
        res.send('nope, not a match');
    }
});



STOP! Check your work.


We could use Postman, but these are all GET routes, so let's just check in the browser.


  1. First, navigate to http://localhost:3000/any
  2. Then navigate to http://localhost:3000/retrieve
    You should see nope, not a match in the browser



Update user information saved on the session object

You can overwrite a session value somewhere else too, just like any other property on a normal JS object.

In server.js:

// Routes / Controllers 
app.get('/update', (req, res) => {
    req.session.anyProperty = 'something you want it to';
    res.send('This is the route that updates req.session.anyProperty');
});

STOP! Check your work.


  1. First, navigate to http://localhost:3000/update
  2. Then navigate to http://localhost:3000/retrieve
    You should see it matches! cool in the browser

Take a moment to think about how we could use what we've learned so far for user authentication and restricting routes. Well dive into that soon, but setting, getting, and updating our session values and and using conditionals in our routes are the fundamentals that play into it.




Destroy the session

Lastly, you can forcibly destroy a session before a user closes their browser window. (Logout? Hint hint!)

In server.js:

// Routes / Controllers
app.get('/destroy', (req, res) => {
    req.session.destroy((error) => {
        if (error) {
            res.send(error);
        } else {
            res.send({
                success: true;
            });
        }
    });
});



STOP! Check your work.


First, navigate to http://localhost:3000/destroy
You should see success: true in the browser.




Express - Authentication Continued: Bcrypt

Lesson Objectives

  1. Explain what bcrypt does
  2. Include bcrypt package
  3. Hash a string using bcrypt
  4. Compare a string to a hashed value to see if they are the same



Explain what bcrypt does

bcrypt is a package that will encrypt passwords so that if your database gets hacked, people's passwords won't be exposed.




Include bcrypt package

In server.js:

// Dependencies 
const bcrypt = require('bcrypt');



Hash a string using bcrypt

bcrypt does this thing called "salting" a string. It requires you to generate a salt which is used in the encryption process. This must be generated each time you hash a string. If you don't do this, the same string will get hashed to the same value each time. If this were to happen, someone with a common password could hack the database and see who else's hashed password had the same value as theirs and know that they have the same password as them.




Here's the code for hashing a string:

const hashedString = bcrypt.hashSync('yourStringHere', bcrypt.genSaltSync(10));



Let's add it to a route to see how it works:

// Routes / Controllers
app.get('/hashed', (req, res) => {
    const hashedString = bcrypt.hashSync('example', bcrypt.genSaltSync(10));
    res.send(hashedString);
})

STOP! Check your work.



  1. Navigate to http://localhost:3000/hashed
    You should see a hashed string in the browser. Something like this: $2b$10$97Gy8GBjTJsggdeUpfGcsud6urcaUP2YntkL4y3lAUaFn2.nr/Yxq
  2. Refresh your page.
    Notice how your hashed string changed, even though we're still passing in the same example string.

Compare a string to a hashed value to see if they are the same

Because the same string gets encrypted differently every time, we have no way of actually seeing what the value of the string is. We can compare it to another string and see if the two are "mathematically" equivalent.




Here's the code to compare a string:

bcrypt.compareSync('yourGuessHere', hashedString);

This evaluates to true or false.




Let's create a route to see how it works:

// Routes / Controllers
app.get('/compare', (req, res) => {
	const hashedString = bcrypt.hashSync('example', bcrypt.genSaltSync(10));
	const isSameString = bcrypt.compareSync('yourGuessHere', hashedString);
	res.send(isSameString);
});



STOP! Check it out


Navigate to http://localhost:3000/compare
You should see false in the browser, because 'example' is not the same as 'yourGuessHere`.


Let's update our code to make it return true. Change yourGuessHere to example the guess will match our already hashed string:

// Routes / Controllers
app.get('/compare', (req, res) => {
    const hashedString = bcrypt.hashSync('example', bcrypt.genSaltSync(10));
    // const isSameString = bcrypt.compareSync('yourGuessHere', hashedString)
    const isSameString = bcrypt.compareSync('example', hashedString);
    res.send(isSameString);
});

STOP! Check it out


Navigate to http://localhost:3000/compare
You should see true in the browser, because 'example' is the same as 'example`

Take a moment to think about how bcrypt can help us protect users passwords (we should never store an un-hashed password in our database) and how it can help us check to make sure the password a user is trying to log in with matches the hashed password we have stored in the database.




References