Node.js Server & Authentication Basics: Express, Sessions, Passport, and cURL

Step 1) Set up the file structure

workspace $ mkdir authTut
workspace $ cd authTut
authTut $ mkdir server
authTut $ mkdir client

Step 2) Initialize npm and install express in the /server folder

authTut $ cd server
server $ npm init -y
server $ npm install express --save
server $ touch server.js
- /authTuts
- /server
- /node_modules
- server.js
- package.json
- /client

Step 3) Create the server and run it

server $ node server.js
Listening on localhost:3000

Step 4) Add our homepage route at ‘/’

The ‘req’ and ‘res’ parameters handed to our app.get(‘/’) callback function are the ‘request’ and ‘response’ objects generated from the request headers that came in.
Listening on localhost:3000
^C
server $ node server.js
Listening on localhost:3000
server $ cd ..
authTut $ cd client
client $ cURL -X GET http://localhost:3000/
you just hit the home page

Step 5) Add nodemon

$ npm install -g nodemon
^C
server $ nodemon server.js
client $ curl -X GET http://localhost:3000 -v
Note: Unnecessary use of -X or --request, GET is already inferred.
* Rebuilt URL to: http://localhost:3000/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
< Content-Length: 66
< ETag: W/"42-Ybeup68SVZ+Id3fqVh25rCkXfno"
< Date: Sun, 29 Oct 2017 19:58:38 GMT
< Connection: keep-alive
<
You hit the home page without restarting the server automatically
  1. cURL is tells us we don’t need to pass the -X GET as that is the default for cURL. I wanted to be explicit with this tutorial however.
  2. “rebuilt URL to…” let’s you know cURL added a slash at the end of the URL.
  3. “Trying ::1…” is the IP address that the URL resolved to.
  4. The next line is the port we connected to, which you notice is the port we specified when we created the server.
  5. > indicates data cURL has sent to the server.
  6. < indicates data cURL has received from server.
  7. Lastly, you see the response text that the server sent

Step 6) Install uuid to automatically generate unique strings

server $ npm install --save uuid
client $ curl -X GET http://localhost:3000
Hit home page. Received the unique id: 044e0263-58b7-4c7f-a032-056cd81069e3

Step 7) Add and configure express-session

server $ npm install express-session --save
  1. Include express-session
  2. Add/configure our app to use the session middleware with a unique session id we generate. We will log the request.sessionID object before and after the middleware is used.
  3. Remove the id we generated/sent to the client
curl -X GET http://localhost:3000 -v
...
< set-cookie: connect.sid=s%3A5199f3ed-3f5a-4478-aed7-fab9ce6ca378.DjQlJ%2F1t%2F00RAfIs5yW6CEsVUXM25aMclq7VGzxVnoY; Path=/; HttpOnly
...
Inside the session middleware
undefined
Inside the homepage callback function
5199f3ed-3f5a-4478-aed7-fab9ce6ca378
client $ curl -X GET http://localhost:3000 -c cookie-file.txt
curl -X GET http://localhost:3000 -b cookie-file.txt -v
...
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.54.0
> Accept: */*
> Cookie: connect.sid=s%3Ade59a40f-6737-4b8d-98bf-bf2bb8495118.e0pWTi2w8%2FAAOKxKgKDBdu99JnspruYSEgLSV3tvxX4
...
Inside the homepage callback function
de59a40f-6737-4b8d-98bf-bf2bb8495118
^C
server $ nodemon server.js
Listening on localhost:3000
client $ curl -X GET http://localhost:3000 -b cookie-file.txt
Listening on localhost:3000
Inside the session middleware
de59a40f-6737-4b8d-98bf-bf2bb8495118
Inside the homepage callback function
ac656d2a-9796-4560-9dbf-73996a1853f8
  1. Server is restarted and session memory is wiped.
  2. We send our cURL request to the server along with our session id
  3. The server receives the requests, and the session middleware can’t find the session id in memory, so it call the genid function
  4. The genid function logs that we are inside the session middleware and it logs the request object’s session id. Since we sent the session id in our cURL request, the request object was actually instantiated with that session id. However, this session id is overwritten by the return value of the genid function.
  5. When the session middleware is done overwriting the session id we sent, control is handed over to the callback function within app.get(), where we log that we are inside the hompage callback function and log the new id.
client $ curl -X GET http://localhost:3000 -b cookie-file.txt
Inside the session middleware
de59a40f-6737-4b8d-98bf-bf2bb8495118
Inside the homepage callback function
b02aa920-7031-427f-ac2e-b82f21140002
client $ curl -X GET http://localhost:3000 -c cookie-file.txt
Inside the session middleware
undefined
Inside the homepage callback function
74f37795-6fcf-4300-beb9-3de41395eafe
...
#HttpOnly_localhost FALSE / FALSE 0 connect.sid s%3A74f37795-6fcf-4300-beb9-3de41395eafe.5mblOCOvpwAMh7bNuTZ9qyloG5UOcIczep5GjMnVEi8
server $ npm install session-file-store --save
server $ nodemon --ignore sessions/ server.js
^C
server $ npm run dev:server
client $ curl -X GET http://localhost:3000 -c cookie-file.txt
^C
server $ npm run dev:server
client $ curl -X GET http://localhost:3000 -b cookie-file.txt

Authentication

Step 1) Add login endpoints

Step 2) Configure Express to be able to read the POST data

curl -X POST http://localhost:3000/login -b cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}'
curl -X POST  http://localhost:3000/login -b cookie-file.txt -H "Content-Type: application/json" -d "{\"email\":\"test@test.com\", \"password\":\"password\"}"
  1. We’re now using -X POST instead of -X GET
  2. We’ve added the -H flag to set the header content-type to application/json
  3. We pass the -d flag in along with the data that we want to send. Note that it is wrapping in single quotes
Inside POST /login callback function
undefined
server $ npm install body-parser --save

Step 3) Add and configure Passport.js

server $ npm install passport passport-local --save
  1. The user is going to POST their login information to the /login route
  2. We need to do something with that data. This is where passport comes in. We can call passport.authenticate(‘login strategy’, callback(err, user, info) ). This method takes 2 parameters. Our ‘login strategy’ which is ‘local’ in this case, since we will be authenticating with email and password (you can find a list of other login strategies using passport though. These include Facebook, Twitter, etc.) and a callback function giving us access to the user object if authentication is successful and an error object if not.
  3. passport.authenticate() will call our ‘local’ auth strategy, so we need to configure passport to use that strategy. We can configure passport with passport.use(new strategyClass). Here we tell passport how the local strategy can be used to authenticate the user.
  4. Inside the strategyClass declaration, we will take in the data from our POST request, use that to find the matching user in the database and check that the credentials match. If they do match, passport will add a login() method to our request object, and we return to our passport.authenticate() callback function.
  5. Inside the passport.authenticate() callback function, we now call the req.login() method.
  6. The req.login(user, callback()) method takes in the user object we just returned from our local strategy and calls passport.serializeUser(callback()). It takes that user object and 1) saves the user id to the session file store 2) saves the user id in the request object as request.session.passport and 3) adds the user object to the request object as request.user. Now, on subsequent requests to authorized routes, we can retrieve the user object without requiring the user to login again (by getting the id from the session file store and using that to get the user object from the database and adding it to our request object).
  1. At the top of the file we are requiring passport and the passport-local strategy.
  2. Going down to the middle of the file, we can see that we configure our application to use passport as a middleware with the calls to app.use(passport.initialize()) and app.use(passport.session()). Note, that we call this after we configure our app to use express-session and the session-file-store. This is because passport rides on top of these.
  3. Going further down, we see our app.post(‘login’) method immediately calls passport.authenticate() with the local strategy.
  4. The local strategy is configured at the top of the file with passport.use(new LocalStrategy()). The local strategy uses a username and password to authenticate a user; however, our application uses an email address instead of a username, so we just alias the username field as ‘email’. Then we tell the local strategy how to find the user in the database. Here, you would normally see something like ‘DB.findById()’ but for now we’re just going to ignore that and assume the correct user is returned to us by calling our users array containing our single user object. Note, the ‘email’ and ‘password’ field passed into the function inside new LocalStrategy() are the email and password that we send to the server with our POST request. If the data we receive from the POST request matches the data we find in our database, we call the done(error object, user object) method and pass in null and the user object returned from the database. (We will make sure to handle cases where the credential don’t match shortly.)
  5. After the done() method is called, we hop into to the passport.authenticate() callback function, where we pass the user object into the req.login() function (remember, the call to passport.authenticate() added the login() function to our request object). The req.login() function handles serializing the user id to the session store and inside our request object and also adds the user object to our request object.
  6. Lastly, we respond to the user and tell them that they’ve been authenticated!
client $ curl -X POST  http://localhost:3000/login -c cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}'You were authenticated & logged in!
Inside session middleware genid function
Request object sessionID from client: undefined
Inside POST /login callback
Inside local strategy callback
Local strategy returned true
Inside passport.authenticate() callback
req.session.passport: undefined
req.user: undefined
Inside serializeUser callback. User id is save to the session file store here
Inside req.login() callback
req.session.passport: {"user":"2f24vvg"}
req.user: {"id":"2f24vvg","email":"test@test.com","password":"password"}

Step 4) Add a route that requires authorization

client $ curl -X GET http://localhost:3000 -c cookie-file.txt
You got home page!
client $ curl -X GET http://localhost:3000/authrequired -b cookie-file.txt -L
You got home page!
#first request to the home page
Inside session middleware genid function
Request object sessionID from client: undefined
Inside the homepage callback
e6388389-0248-4c69-96d1-fda44fbc8839
#second request to the /authrequired route
Inside GET /authrequired callback
User authenticated? false
curl -X POST http://localhost:3000/login -b cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}'
You were authenticated & logged in!
curl -X GET http://localhost:3000/authrequired -b cookie-file.txt -L
you hit the authentication endpoint
Inside POST /login callback
Inside local strategy callback
Local strategy returned true
Inside passport.authenticate() callback
req.session.passport: undefined
req.user: undefined
Inside serializeUser callback. User id is save to the session file store here
Inside req.login() callback
req.session.passport: {"user":"2f24vvg"}
req.user: {"id":"2f24vvg","email":"test@test.com","password":"password"}
Inside deserializeUser callback
The user id passport saved in the session file store is: 2f24vvg
Inside GET /authrequired callback
User authenticated? true

Step 5) Hook up a database and handle incorrect credentials

authTuts $ mkdir db
authTuts $ cd db
db $ npm init -y
db $ npm install json-server --save
db $ touch db.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"json:server": "json-server --watch ./db.json --port 5000"
},
db $ npm run json:server
server $ npm install axios --save
client $ curl http://localhost:3000/login -c cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}' -L
you hit the authentication endpoint

Step 6) Handling encrypted passwords

server $ npm install bcrypt-nodejs --save
client $ curl http://localhost:3000/login -c cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}' -L
you hit the authentication endpoint

You did it!

  • Express and how it uses middleware
  • How session data is stored and retrieved both on the server and client
  • Passport’s authentication flow and how to use it for authorization as well
  • How to use bcrypt to check plaintext against hashed passwords

--

--

--

Building tools for writers

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Up and Running with React, Flask and MongoDB in 5 minutes or Less

Valuation of NFTs

ReactJS — What are props?

The easiest way to improve your Vue.js application. Part 1

Clear though area rest two understand.

Chill and Cozy p5.js & Processing Tutorials on Twitch

A screenshot of a past Cozy Coding stream that features a shaders demo in the p5 web editor.

TailwindCSS with React.js (pt. 1)

Create a SharePoint File Picker in React with Microsoft Graph and Fluent UI

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Evan Gow

Evan Gow

Building tools for writers

More from Medium

What is Node.

HTTP Logging with Morgan and Winston

Mongoose, an ORM (Object-relational mapping), is a fantastic NPM(Node Package Manager) library.

What is node.js and how it works Event loop