# Express Router, Authentication & Testing

# Static Content

We can make a web application out of our REST-API. First you need to create a folder at the root of your application, and name it for example "public". When you add an index.html file there, with the example below it will served when you send a GET request to our server.

import express from 'express'

const server = express()

server.use(express.static('public'))

server.get('/route', (req, res) => {

res.send('OK')

})

server.listen(PORT, () => console.log('Listening to port', PORT))

# Exercise 1: Static

Let's continue improving the Students API we created in the last lecture.

Add a static info page to your API.

The page should be reachable from the root path  /  and it should include some information about all the endpoints in the API.

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Router

# Express Router

Until now we have been building our routes to index.js. This is fine for VERY small applications, but we'd like to keep our code readable even if our application grows in size.

__Routers__  are Express classes that are dedicated to route handling. We can use them to group routes to meaningful units that share functionality.

Routers do not require any installation since they are native express classes.

The following application, while still being extremely simple, already has a problem: index.js file is doing several things.

It handles two kinds of routes and starts the server. Usually it also adds middleware and some configuration.

Most of the time it is best to separate the route handling to dedicated routers.

import express from 'express'

import authors from './approvedAuthors.js'

import articles from './articlesDao.js'

const server = express()

server.get('/authors', (req, res) => {

res.send(authors)

})

server.get('/authors/:id', (req, res) => {

const author = authors.find(author => author.id = req.params.id)

res.send(author)

})

server.get('/articles', (req, res) => {

res.send(articles.getAll())

})

server.post('/article', (req, res) => {

const { author, article } = req.body

const newArticle = articles.add({ author, article})

res.send(newArticle)

})

server.listen(3000, () => console.log('Listening to port 3000'))

We create two routers, one for each logical unit:  _authorsRouter.js_  and  _articlesRouter.js_

The only difference in the route definition is in the route paths.

import express from 'express'

import authors from './approvedAuthors.js'

const router = express.Router()

router.get('/', (req, res) => {

res.send(authors)

})

router.get('/:id', (req, res) => {

const author = authors.find(author => author.id = req.params.id)

res.send(author)

})

export default server

import express from 'express'

import articles from './articlesDao.js'

const router = express.Router()

router.get('/', (req, res) => {

res.send(articles.getAll())

})

router.post('/', (req, res) => {

const { author, article } = req.body

const newArticle = articles.add({ author, article})

res.send(newArticle)

})

export default router

Then we define  _index.js_  to use those routers like any middleware.

Each route gets a path designation that will be used as  _prefix_  in the router.

import express from 'express'

import authorsRouter from './authorsRouter.js'

import articlesRouter from './articlesRouter.js'

const server = express()

server.use('/authors', authorsRouter)

server.use('/articles', articlesRouter)

server.listen(3000, () => {

console.log('Listening to port 3000')

})

# Exercise 2: Students Router

Let's continue improving the Students API we created on the last lecture.

Add a  _studentRouter.js_  file that exports a router with all the five routes the app currently has.

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Authentication & authorization

# Token based login

* Currently our applications are accessible for anyone, which often is not a desired state of things.
* If you don't restrict access to your endpoints, something like  [this](https://mastodon.gamedev.place/@badlogic/111246798083590676)   [might happen](https://firesky.tv/) !!!
* Authentication & Authorization help here
  * Authentication: Verifying that you are indeed you
  * Authorization: Determining access rights based on your privileges
* We shall first look at password authorization, and later token-based authentication.

# Storing passwords

# Password Hashing

Passwords should never be stored as plain text in to a database. Instead a password  _hash_  should be stored instead.

_Hash function_  maps data of any size (in our case a password) to a  _hash _ of fixed length.

Hash functions are  _one-way functions_ , which means they can not be reversed. This means that you can not deduce the original password from the hash.

We can use (a variant of) the original hash function to compare a password and the hash and deduce if the hash  _matches_  the password or not.

# Salting

Bare hash functions produce identical results from identical inputs.

To add complexity, a some random data is added to the input. This data is called  _salt_  and the process of adding the salt is called  _salting_ .

This means two identical passwords produce different hashes, since both have unique salt.

Salt can be stored in the hash or it can be stored separately. The hash function needs to know the salt in order to verify passwords against the hash.

# Hashing a password

We will use Argon library to do our hashing and comparing. Install Argon with   __npm install argon2__

Using Argon to hash passwords is very straightforward. Salting is done automatically (although you can configure it in more detail if you wish to do so) and the salt is added to the hash.

import argon2 from 'argon2'

const password = process.argv[2]

argon2.hash(password)

.then(result => console.log(result))

You can of course also use  _async/await_  syntax instead of  _.then()

# Comparing hashes

Notice if you run the example from the previous slide multiple times, you always get a different result. In order to verify that a particular password matches the hash, Argon's  _verify_  function is used.

Verify returns a promise that resolves to either  _true,_  if the password matches the hash, or  _false_  if it doesn't.

import argon2 from 'argon2'

const hash = '$argon2id$v=19$m=4096,t=3,p=1$+OM8yk1Kd3M/709t0Hy1vg$E3aii3UmOQOp6jFJVd9xDpakMOF1O6TDd1gS1i/98HE'

const password = process.argv[2]

argon2.verify(hash, password)

.then(passwordMatchesHash => console.log(passwordMatchesHash ? 'Correct' : 'Incorrect'))

# Exercise 3: Registration

Create and attach a new router,  _userRouter.js_  that has a single endpoint:  POST /register .

The endpoint should

expect a request body with two parameters,  _username _ and  _password_ .

create a hash from the password using the Argon2 library.

store the username and the hash in an in-memory storage (e.g. let users = [ ... ]) and log the result to the console.

return a response with status code 201 (Created) on success

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Exercise 4: Login

Add another endpoint to the user router:  POST /login  that also expects two request body parameters,  _username _ and  _password_ .

The endpoint should

check that the user exists in the in-memory storage.

If the user exists, it should use the Argon2 library to verify that the given password matches the stored hash.

If they match, it should return a response with status code 204 (No Content).

If the user does not exist, or the password doesn't match the hash, it should return a response with status code 401 (Unauthorized).

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Environment variables

# dotenv Library

With dotenv library, we can create environment variables that can be loaded into process.env. We can access the environment variables in our application with  process.env.{variable\_name}.

To use dotenv

Install dotenv package with   __npm install dotenv__

Create a   __.env __  file in to the root folder of your application where the values are declared

Import the dotenv configuration in the projectimport 'dotenv/config'

Access the variables defined in  __.env__   fileconst PORT = process.env.PORT

# .env File

The custom environment variables are declared in the .env file. The file must be located in the folder from where the program is ran. This is usually the root folder.

The variables are declared as in the example below, separated by line changes. All values are  _strings _ (even if JS parses some automatically).

Notice there are no spaces around the equals (=) sign, nor any kind of quotation marks.

API\_URL=https://cataas.com

PORT=3000

# dotenv in Development

* In many cases the environment variables are configured on the platform from where the program is ran. In those cases you might want to run dotenv only in development mode.
* Install as dev dependency   __npm install --save-dev dotenv__
* Create  __.env__  file as usual.
  * Optionally include ENVIRONMENT=development variable
* Run your application from config.json script that preloads dotenv"dev": "nodemon -r dotenv/config index.js"
* Access environment variables as usualconst PORT = process.env.PORT

# Exercise 5: Environmental Login

Add an admin login to the Students API. We want to store admin credentials in environment variables.

Add dotenv library as dependency.

Add a .env  file that defines an admin username and an admin password hash.

Add an endpoint  POST /admin  that also expects two request body parameters,  _username _ and  _password_ . The endpoint should

check that the username matches the one defined in the .env file

check that the password matches the hash defined in the .env file

if they match, it should return a response with status code204 (No Content)

if they do not match, it should return a response with status code 401 (Unauthorized)

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Token-based authentication

# JSON Web Token

After verifying the identity of our user, we create a  _token_  that we send to the client. The client stores that token and attaches it to all future requests. This token is enough to identify the request as authorized.

Since the tokens are signed by us, we can also include data (such as user id) in the token. On all future requests we can rely that the id is correct since the JWT will be verified on every request.

Tokens can (and should be) equipped with expiration dates.

# Creating a Token

Install the JSON Web Token package as dependency  npm install jsonwebtoken

Tokens are created with the  _sign_  function that takes two parameters: _Payload:_  the data we want to include in the token. It can be empty. _Secret_ : either a string or a private key.

It can also accept an optional  _options_  parameter. In that we can define the expiration date, compression algorithm, or many other things.

import 'dotenv/config'

import jwt from 'jsonwebtoken'

const payload = { username: 'sugarplumfairy' }

const secret = process.env.SECRET

const options = { expiresIn: '1h'}

const token = jwt.sign(payload, secret, options)

console.log(token)

The created JWT has three parts separated by period: header, payload and signature.

# Exercise 6: Create a Token

Write a simple command line program that prints JSON Web tokens. Use the default algorithm (SHA256). Set the tokens to expire in fifteen minutes and include a payload of some JSON object.

Copy your JWT and paste it to  [https://jwt.io](https://jwt.io)  debugger. Verify that the debugger shows correct algorithm, data, and expiration date (iat).

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Verifying a Token

Tokens are verified using the  _verify_  function that takes two parameters:

_Token_ : the token to be verified

_Secret_ : the secret that was used to create the token

The verify function can also accept an optional  _options_  parameter.

The verify function returns a  _decoded_  token. Since it has been verified against our secret, we know that the information has not been altered.

If the token is invalid, the verify function  _throws an error_ . If this error is not caught, the program crashes.

# Exercise 7: Verify a Token

Write a simple command line program that verifies JSON Web tokens.

The program should print the contents of the verified token. If the token is not valid, the program should print an error message and exit gracefully.

Create a JWT in  [https://jwt.io](https://jwt.io)  debugger. Remember to set your secret value. Use your program to verify the token and see that the data entered is as it should be.

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Bearer Tokens

When a user has received a token, it can be used to authenticate all future requests.

Do this by adding the field  _Authorization _ to the request header.

The value is "Bearer " plus the token.

This header can be added to requests generated by Fetch, Axios, or any other JS request library, or by API clients like Postman or Insomnia.

Then protected routes can check that the authorization header is valid, and if not, send an error message. This is usually done using authorization middleware.

# Authentication Middleware

A common way is to have user routes, such as login, logout or user creation in a separate router, which requires no token.

Then all protected routes are set to use the authentication middleware.

The request parameter  _user_  is set to the verified token data and can then be used in the actual route.

const authenticate = (req, res, next) => {

const auth = req.get('Authorization')

if (!auth?.startsWith('Bearer ')) {

return res.status(401).send('Invalid token')

}

const token = auth.substring(7)

const secret = process.env.SECRET

try {

const decodedToken = jwt.verify(token, secret)

req.user = decodedToken

next()

} catch (error) {

return res.status(401).send('Invalid token')

}

}

router.get('/protected', authenticate, (req, res) => {

res.send(\`${req.user.username} accessed protected route\`)

})

# TypeScript: Extending Request

Let's look at the following example, written in JavaScript. There is a middleware that adds a  _username _ property to the request object. Then the endpoint reads the username property and uses it to construct a response.

What happens when we write the same code in TypeScript?

import express from 'express'

const server = express()

const middleware = (req, \_res, next) => {

req.username = 'sugarplumfairy'

next()

}

server.use(middleware)

server.get('/', (req, res) => {

res.send(\`Welcome, ${req.username}!\`)

})

server.listen(3000)

Property 'username' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'

import express, { Request, Response, NextFunction } from 'express'

const server = express()

const middleware = (req: Request, res: Response, next: NextFunction) => {

req.username = 'sugarplumfairy'

next()

}

server.use(middleware)

server.get('/', (req, res) => {

res.send(\`Welcome, ${req.username}!\`)

})

server.listen(3000)

Property 'username' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'

We are trying to set a value to request property "username", but request objects are well defined objects that have no such property.

We can not add or use properties that an object does not have. This is part of the type safety that TypeScript is here to enforce. If an object is of type Request, we know exactly what the properties are.

The solution is to create an interface that extends the existing Request type. The new interface will have all the properties the Request has, plus the ones we define ourself.

interface CustomRequest extends Request {

username?: string

}

Since the interface extends Request, CustomRequest objects can be used anywhere where Request object is required.

Notice that the additional properties must be optional, signified by the question mark in the name. Otherwise existing Request objects can not be cast as CustomRequest objects.

This example has only a single string, but the extended object can be as complex as needed.

interface CustomRequest extends Request {

username?: string

}

Now we can use the "username" property when we declare that we are using CustomRequest instead of basic Request object.

import express, { Request, Response, NextFunction} from 'express'

const server = express()

interface CustomRequest extends Request {

username?: string

}

const middleware = (req: CustomRequest, res: Response, next: NextFunction) => {

req.username = 'sugarplumfairy'

next()

}

server.use(middleware)

server.get('/', (req: CustomRequest, res: Response) => {

res.send(\`Welcome, ${req.username}!\`)

})

server.listen(3000)

# Exercise 8: Securing a Route

Modify the Students API  /register  and  /login  routes so that on success they return a response with status code 200 and a JWT with username as payload.

Secure all the routes in the  _students _ router so that they require the user to be logged in to use the routes.

Extra: Also modify the  /admin  route to return a JWT. Secure the POST, PUT and DELETE routes to require that in addition to being logged in, the user also needs to be an admin.

Hint: Here you have some resources on sending a token with Postman.

[https://learning.postman.com/docs/sending-requests/authorization/](https://learning.postman.com/docs/sending-requests/authorization/)

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

# Supertest

# Testing APIs with Supertest

Supertest is a library for testing API's. Let's see how to use it with Jest to test an Express app. Install supertest with

npm install --save-dev supertest

When using ESLint, also add "jest": true to .eslintrc's "env" object

_index.js_

import express from 'express'

const server = express()

server.get('/', (req, res) => {

res.send('Hello World!')

})

server.listen(3000, () => {

console.log('Listening to port 3000')

})

When using TypeScript, also install the required types from @types/supertest and @types/jest

Also, install the ts-jest package, and add to package.json:

"jest": {

"preset": "ts-jest",

"testEnvironment": "node"

},

We need to import our express application to the tests.

We will separate our application in two files. The  _server.js_  defines the API server, and  _index.js_  only starts the application.

Now we can test the server with Supertest. The startup file has no functionality we want to test.

_index.js_

import server from './server.js'

server.listen(3000, () => {

console.log('Listening to port 3000')

})

_server.js_

import express from 'express'

const server = express()

server.get('/', (req, res) => {

res.send('Hello World!')

})

export default server

We now create a new test file and import our server there.

We use Supertest  _request_  to get responses from our server. These responses can be tested just like any other Jest test.

_test/server.test.js_

import request from 'supertest'

import server from '../src/server.js'

describe('Server', () => {

it('Returns 404 on invalid address', async () => {

const response = await request(server)

.get('/invalidaddress')

expect(response.statusCode).toBe(404)

})

it('Returns 200 on valid address', async () => {

const response = await request(server)

.get('/')

expect(response.statusCode).toBe(200)

expect(response.text).toEqual('Hello World!')

})

})

# Common Resources

[https://www.npmjs.com/package/jest](https://www.npmjs.com/package/jest)  → Official repository

[https://www.npmjs.com/package/supertest](https://www.npmjs.com/package/supertest)  → Official repository

[https://jestjs.io/docs/api](https://jestjs.io/docs/api) 	→ Jest API Docs. Very handy!

[https://github.com/visionmedia/supertest](https://github.com/visionmedia/supertest)  → Supertest's github repository. Includes guides for basic use cases.

# Exercise 9: Test with Supertest

Use Supertest with Jest to create tests for the Students API  /user/register  and  /user/login  routes.

---

Solution: https://gitlab.com/bctjs/week3-day2/-/blob/master/examples/day2/countdown.js

