Fully Serverless DERN Stack TODO App Pt. 2 - Building out our API
Tags: javascript, tutorial, devops, node
Published: 2022-03-02 19:30:57

## Part 2 - Building out our API & Auth system

[Part. 1](https://dev.to/adamkatora/fully-serverless-dern-stack-todo-app-pt-1-dynamodb-express-react-node-3dlc)

If you're just joining us, in [Part 1](https://dev.to/adamkatora/fully-serverless-dern-stack-todo-app-pt-1-dynamodb-express-react-node-3dlc) of this series, we setup a simple express.js application, then used Claudia.js to deploy our app to AWS.  

Here in Part 2, we'll be building out enough of our application that by the end you'll have a small, but functional, REST API. Since Part 1. was a lot of boilerplate Claudia.js setup, I've tried to get this Part 2 out as quickly as possible so that you can start to get an idea of what our final app will look like.

As such, I haven't been able to fully go through this write-up myself to ensure that there's no bugs in the code, and add in helpful screenshots. That'll be coming soon. I'm going to make sure the [Github repo](https://github.com/akatora28/serverless-dern-todo-pt2/) for this write-up is up to date first, so if you run into any issues, try checking there first for working code examples.

With all that out of the way, let's move on to the fun stuff,  developing some features for our app. Mainly, a simple Auth system. We'll start by adding the Dynamoose package so writing some data models. We'll also add morgan, a logger middleware so that we can get info about incoming requests in the console.

From the `/backend` folder run the following:  
```bash
npm install dynamoose morgan
```

Next, inside the `/backend/src` create a `models` directory where we'll store our dynamoose models.

```
cd src
mkdir models
```

We're going to try to keep our app simple, so we'll create 2 models. 1.) Will be a User model, with a _very_ (read NOT production ready) basic auth system. 2.) Will be a Todo model to store information about User's Todos.

From inside the models folder create two new files for each of the models. I like to follow a `[ModelName].model.js` naming convention in my Express.js apps.

```bash
cd models
touch User.model.js
touch Todo.model.js
```

Now, it's time to build out our models. If you've used Mongoose before, the syntax and schema of Dynamoose models should look very familiar to you.

Type the following code for our User model.

***User.model.js***
```js
const dynamoose = require("dynamoose");

const userSchema = new dynamoose.Schema({
    "id": String, // UUIDv4 ID
    "username": String,
    "password": String,
}, {
    "timestamps": true
})

const User = dynamoose.model("User", userSchema)

module.exports = User
```

We start by importing the dynamoose library with `require("dynamoose")`. Next, we define our model's schema with the `dynamoose.Schema()`. The first Object we pass into `dynamoose.Schema()` contains all the fields and their associated "attribute types" (aka data types) for our model. 

You can read about the available [attribute types here](https://dynamoosejs.com/guide/Schema#attribute-types).  

For right now, we're just going to create fields for `id`, `username`, and `password`. 

___
I've mentioned this already, and I think it goes without saying but just to cover all my bases here, **I wouldn't use this auth implementation in a production app**. There's much better and more secure IdP services out there for developers. AWS has their [Cognito](https://aws.amazon.com/cognito/) IdP service, and [Auth0](https://auth0.com/) is another good choice. Both offer a fairly generous free-tier to allow you to get started quickly and eventually grow into a paid plan.
___

We also pass a second object to the `.Schema()` method, with some additional Schema Settings. We're setting "timestamps" to true which will automatically add createdAt & updatedAt timestamps.

Finally, we use the `dynamoose.model()` method, to create a new const `User`. The first param passed to `.model` is a string. This is what our model will be called. The second param we pass to `.model` is the object containing our SchemaDefinition and SchemaSettings, which in our case we stored in the `userSchema` const.

At the bottom of the file, we have a standard `module.exports` so that we can import the `User` model in other files. 

With that created. Let's add the following to our `Todo.model.js` file.

***backend/src/models/Todo.model.js***
```js
const dynamoose = require("dynamoose");

const todoSchema = new dynamoose.Schema({
    "id": String, //UUIDv4
    "user": Object,
    "title": String,
    "notes": String,
    "dueDate": String,
    "status": String,
}, {
    "timestamps": true
})

const Todo = dynamoose.model("Todo", todoSchema)

module.exports = Todo
```

Our `Todo` model is very similar to our `User` model with one major difference. We added a field for `user` with a type of `Object`. We might end up changing this around later on, but that's one of the beauties of NoSQL databases, we don't have to get bogged down in too much data-modelling early on.

Now that we have our Models in place, we need to start building out how our API will interact with our models. I like to structure my Express.js apps in a bit of an MVC pattern (in this case React will be our **V** - view layer), and also create "Service Layers". If those two things don't make sense to you, no worries, just follow along and hopefully the project structure and code should help you make sense of those terms as we go along.  

Also, if you've been following along this far, I'm going to assume you're comfortable with making new directories & files, so I'll just explain what new dirs and files we're creating, then at the end show the project structure instead of showing the bash command to create each new file.

Back inside the `/src` directory, make directories for `routes`, `controllers`, and `services`. Inside `/src/routes` create an `index.js` file and an `auth.routes.js` file. Inside the `/src/contollers` directory create a file `Auth.controller.js`. Inside the `/src/services` directory create an `Auth.services.js` file.

With all of those files created, this is what our project structure should look like now:
```bash
backend/
    - node_modules/
    - src/
        - controllers/
            - Auth.controller.js
        - models/
            - Todo.model.js
            - User.model.js
        - routes/
            - Auth.routes.js
            - index.js
        - services/
            - Auth.service.js
        - app.js
        - app.local.js
    - claudia.json
    - lambda.js
    - package-lock.json
    - package.json
```

With those files created, let's get our router setup. 

Let's start by editing our `src/app.js` file. Make the following changes so that your app.js file looks like this:

***/src/app.js***
```js
const express = require("express")
const app = express()

// morgan for logging
const morgan = require("morgan")
app.use(morgan('dev'))

// Import Routes
app.use(require("./routes"))

module.exports = app;
```

First, we start by adding the morgan logging middleware. This will handle automatically logging to the console what requests our app receives, useful for both development and catching things that go wrong in production.

Next, we tell our app to handle all routes from our `./routes/index.js` file. You'll notice that we didn't explicitly reference the `/.routes/index.js` file though, just the dir name. 

Let's go ahead and implement our routes file now. Inside `/src/routes/index.js` add the following code:

***/src/routes/index.js***
```js
const router = require('express').Router();
const authRoutes = require('./Auth.routes')

// Moved our API Root GET "Hello world!" here
router.get('/', (req, res) => res.send('Hello world!'))

// Import Auth routes
router.use('/api/auth', authRoutes)

module.exports = router;
```
We've moved our API Root GET request to this file to keep it organized with the other routes. We'll keep it now for testing, 

In the second line of `/src/routes/index.js` we require() our `./Auth.routes.js` file and store it as a const, `authRoutes`. We haven't implemented that file yet either, so let's do that now.

Inside `/src/routes/Auth.routes.js` file, add the following code:

***/src/routes/Auth.routes.js***
```js
const router = require("express").Router()

// TODO - implement this route fully
// POST - /api/auth/register
router.post('/register', (req, res) => res.send("/register") )

module.exports = router;
```

This creates a `POST` endpoint for `/api/auth/register` which simply returns a string "/register" back to the requester.

With the boilerplate for our routing system mostly complete. This would be a good time to test everything is working before we continue much further.

Back in Postman, let's first test our "Hello world!" request to make sure that's still working from the new `routes/index.js` file.

Make sure the local dev server is running with:
```bash
npm run dev
```

Then use Postman to make a `GET` request to `http://localhost:3000/` (In part 1 I promoted this to a variable `{{BASE_URL}}`, I'll be referencing that moving forward)

You should see the following output:
```
$ npm run dev

> dern-backend@1.0.0 dev
> nodemon src/app.local.js

[nodemon] 2.0.15
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node src/app.local.js`
App is listening on port 3000.
GET / 200 1.582 ms - 12
```

You'll notice the output is the same as before, except the morgan middleware logged our `GET` request. In Postman you should see the return value of "Hello world!"

Let's also test our `/api/auth/register` endpoint is working. Create a new `POST` request in Postman for that endpoint.

In Postman you should see "/register" as the response value, and the console should have logged the new `POST` request:

```bash
$ npm run dev
...
POST /api/auth/register 200 2.381 ms - 9
```

The next step is to setup our Controllers, these are the **C** in MV**C**. To briefly explain the job of Controllers, they receive the HTTP request data from the application Router. The Controller 
## TODO - Explain this better

Add the following code to our `/src/controllers/Auth.controller.js` file:
***/src/controllers/Auth.controller.js***
```js
// Register New User
exports.register = async function(req, res) {
    // req validation would be handled here
    const newUserInput = req.body

    // TODO - Auth Service Register User

    res.json(newUserInput)
}
```
The controller is mostly a placeholder right now, but we're saving the request body into a const `newUserInput`. However, we haven't implemented the express.json() middleware in order to be able to access the req.body object.

In `/src/app.js` add this to lines 4 & 5  

***/src/app.js***
```js
// Using express.json() to read req.body
app.use(express.json())
```
(If you've previously used body-parser for Express.js this has essentially replaced that)

Next, update the `/src/routes/Auth.routes.js` file to the following to send the request to our new Controller:  

***/src/routes/Auth.routes.js***
```js
const router = require("express").Router()
const authController = require("../controllers/Auth.controller")

// POST - /api/auth/register
router.post('/register', authController.register)

module.exports = router;
```

Since this is the first time in our application that we're dealing with request body data, this is a good opportunity to test that as well.

You should still have a `POST {{BASE_URL}}/api/auth/register` request. Click on the "Body" tab for that request, and click the gray dropdown box that says "none". Change that value from "none" to "raw", then in the Blue Text dropdown that appears, select "JSON".

Set the body value to the following:
```json
{
    "username": "adam",
    "password": "adamPass"
}
```

With all that set, run the request. In the console, you should see our `POST` request logged. Additionally, the API response should just be the request body returned back to you.

With that working, we can now implement the Service Layer of our application. To briefly explain the job of the service layer, the service layer is where the bulk of our application's business logic exists. This is where we'll put our Dynamoose calls to perform CRUD operations, and handle logic for validating users' accounts, passwords, etc.

A major benefit of moving our business logic out of the controller (or even worse, the routes) and into a service layer, is that is makes our code much more modular and re-usable.  

Let's take the Auth service we're about to implement for example. We want Users to be able to register for our app. We also want them to be able to login. However, wouldn't it be a nice feature, if after a User successfully registers for our app, they're automatically logged in.  

If we were to keep all of that logic inside the controllers, we would have to copy/paste the login into the register controller as well. Not terrible at first, but it can quickly become a pain to maintain that duplicate code in two places, and goes directly against the DRY Principle (**D**on't **R**epeat **Y**ourself).  

Again, don't worry if that doesn't all make sense right now, we'll implement the service layer so you can see how it all works together.

We'll need two more packages for our Auth implementation. From the `/backend` folder install the bcryptjs, and uuid packages with the following:
```bash
npm install bcryptjs uuid
```

We'll add the following AWS SDK configuration settings to `/src/app.js`. Below `app.use(express.json())` add the following:
```js
...
// Dynamoose configuration
const dynamoose = require("dynamoose")
dynamoose.aws.sdk.config.update({"region": "us-east-1"});
```

___
**Side Note:** Regarding AWS Authentication & Configuration -  
On my dev machine, I export the Access Key, Secret Key, and Session Token into my terminal, which allows my application to quickly interact with AWS Cli & SDK services without too much configuration. If you know how to do this and can follow along as such, great.  

*This is what you would type into a bash terminal to export those variables:*
```bash
export AWS_ACCESS_KEY_ID="[ACCESS_KEY_ID]"
export AWS_SECRET_ACCESS_KEY="[ACCESS_KEY]"
export AWS_SESSION_TOKEN="[SESSION_TOKEN]"
```

Otherwise, for readers newer to AWS, I think it's probably simpler and more straight forward to configure that information in our app via code.  

A major caveat of doing so is that our application will have to access sensitive information, ie our AWS ACCESS_KEY & SECRET_ACCESS_KEY. You should never hard-code sensitive information like keys & secrets into your application. Later on in this write-up, I install and configure dotenv so we can sign our JWTs with a secret.

You'll need to install with npm the `dotenv` package. Then, update your app.js file to include dotenv and configure it, ideally as early as possible in your application.

```js
// Dotenv config
const dotenv = require('dotenv');
dotenv.config();

dynamoose.aws.sdk.config.update({
    "accessKeyId": process.env.AWS_ACCESS_KEY_ID
    "secretAccessKey": process.env.AWS_SECRET_ACCESS_KEY,
    "region": "us-east-1",
});
```

Don't forget, you'll need a `.env` file in the `/backend` folder with the following values:
```env
AWS_ACCESS_KEY_ID=[YOUR ACCESS KEY]
AWS_SECRET_ACCESS_KEY=[YOUR SECRET KEY]
```

I still have to build out and test a working example for this, but check the [github repo for pt. 2](https://github.com/akatora28/serverless-dern-todo-pt2/) to see the latest code examples if you're running into issues implementing this.
___

Then add the following to the `/src/services/Auth.service.js` file:

***/src/services/Auth.service.js***
```js
// Import our Dynamoose User model, Bcrypt for password hashing and uuidv4
const User = require("../models/User.model")
const bcrypt = require("bcryptjs")
const {v4: uuidv4} = require("uuid")

exports.registerUser = async function(newUserInfo) {
    // newUserInfo is req.body - so it should be a JSON object ie {"username":"adam","password":"adamPass"}

    // First, check is there's already a user registered with this username
    var existingUser
    try {
        // Runs a DynamoDB scan and returns the result
        existingUser = await User.scan({username: {eq: newUserInfo.username}}).exec()
    } catch (err) {
        console.log(err)
        throw new Error(err)
    }
    // If there already is a User, throw an Error
    if(existingUser.count > 0) {
        throw new Error("EXISTING_USER_ERROR")
    } 
    
    // User doesn't already exist, so let's register them
    var newUser 
    try {
        const uuid = uuidv4()
        const salt = await bcrypt.genSalt(10)
        const hashedPass = await bcrypt.hash(newUserInfo.password, salt)
        newUser = await User.create({
            "id": uuid,
            "username": newUserInfo.username,
            "password": hashedPass
        })
    } catch (err) {
        console.log(err)
        throw new Error(err)
    }

    // TODO loginUser(newUser) -> return JWT w/ newUser

    return newUser
}

exports.loginUser = async function(userInfo) {
    // userInfo should be a JSON Object {"username":"adam","password":"adamPass"}
    // First, Check if the User even exists - In contrast to the above, in this case we do want there to be an existing User
    var existingUser
    try {
        existingUser = await User.scan({username: {eq: userInfo.username}}).exec()
    } catch (err) {
        console.log(err)
        throw new Error(err)
    }
    // If User doesn't exist, throw an error
    if(existingUser.count == 0) {
        throw new Error("INVALID_LOGIN_CREDENTIALS")
    }

    // Check if the supplied password matches the bcrypt hashed password saved in the User record
    var validPass
    try {
        // bcyrpt.compare will return true / false depending on if the passwords match
        // User.scan() always returns an array, hence why we specify existingUser[0].password below
        validPass = await bcrypt.compare(userInfo.password, existingUser[0].password)
    } catch (err) {
        console.log(err)
        throw new Error(err)
    }

    // If validPass is false, throw an error
    if(!validPass) {
        throw new Error("INVALID_LOGIN_CREDENTIALS")
    }

    // TODO - JWTs - We do need someway for our user to stay logged in after all

    return {"message": "Login Successful"}
}
```

Update the `/src/controllers/Auth.controller.js` file:
***/src/controllers/Auth.controller.js***
```js
const authService = require('../services/Auth.service')

// Register New User
exports.register = async function(req, res) {
    // req validation would be handled here - We're just assuming the request is properly formed
    // fine for a proof-of-concept, terrible in practice
    const newUserInput = req.body

    var newUser
    try {
        newUser = await authService.registerUser(newUserInput)
    } catch (err) {
        console.log(err)
        if(err.message == "EXISTING_USER_ERROR") {
            return res.status("422").json({"message":"User already exists"})
            // If you don't include the above return, the code will continue executing 
            // and hit the throw new Error("REGISTER_USER_ERROR") below, causing our app to crash
        }
        throw new Error(err)
    }

    res.json(newUser)
}

exports.login = async function(req, res) {
    const userInput = req.body

    var existingUser
    try {
        existingUser = await authService.loginUser(userInput)
    } catch (err) {
        console.log(err)
        if(err.message == "INVALID_LOGIN_CREDENTIALS") {
            return res.status("401").json({"message":"Invalid username or password"})
        }
        throw new Error(err)
    }

    res.json(existingUser)
}
```

Lastly, don't forget to add a `/api/auth/login` endpoint to the `/src/routes/Auth.routes.js` file, add this on lines 7 & 8 below the existing `/api/auth/register` endpoint:

```js
// POST - /api/auth/login
router.post('/login', authController.login)
```

This is the first substantial bit of code we've written, so let's take a moment to examine what everything does. Also, I've written this to use async/await as opposed to callbacks since I think it's cleaning and easier to understand. If you're not familiar with the syntax [here's some documentation that might help clarify](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await)

Starting with the `Auth.service.js` file, we imported our Dynamoose User model that we created earlier, we also imported bcrypt for hashing passwords, and uuidv4 to generate ids for our DynamoDB records.  

Then, we created a function `registerUser` which accepts a single Object, `newUserInfo`, as a parameter. There's no type checking, or input validation implemented, but `newUserInfo` should consist of a string `username` and `password`. Next in the registerUser function, we check if there is already a User registered with the supplied username, if there is we return a named error "EXISTING_USER_ERROR".

If a user doesn't already exist, we precede with User creation by generating a uuid, salting & hashing the new user's password, then finally using the User.create() method (which is part of Dynamoose) to store the new user as a record in our DynamoDB table. 

Once that is complete we return the newUser Object in the response body with a default status code of 200.

You'll notice that above the return line, I left a TODO comment indicating where we'll eventually call the AuthService login function (in this case it's in the same file). We'll be adding in JWT for frontend auth soon, but I wanted to include that to illustrate the benefit of implementing a service layer.

For the loginUser function in our Auth Service, the code is very similar to the registerUser function, except instead of throwing an error if a user exists, we throw an error if the user doesn't exist.

We also use the bcrypt.compare function to see if the user supplied a valid password. Since `Dynamoose.scan()` returns an array, in our case the existingUser variable, we have to specify `existingUser[0].password` when supplying the hashed password to bcrypt, otherwise existingUser.password would be undefined.

In our Auth Controller file, `/src/controllers/Auth.controller.js`, we imported our Auth Service file and saved it as a const `authService`. We then updated, the Controller's `register` function to make a call to the Auth Service's `registerUser` function.

If the Auth Service call returns an "EXISTING_USER_ERROR" error to us, we send a 422 status and error message as a response. An important thing to note about Express is that it will continue to execute code even after a call to `res.send()`, or `res.json()` is made. That is why we include the `return` statement immediately before `res.status("422")...` is called. If we didn't have the return statement, Express would continue to the next line `throw new Error(err)` and throw an error that would crash our app, even though we handled the error correctly.

Try removing the `return` statement from that line and sending a couple test requests if you want to see how that works.

In the Auth Controller `login` function, we make a call to the Auth Service `loginUser` function, and same as with register, either handle the named error, or send the return value of the `authService.loginUser()` call in the response.

The last thing we updated was to add the new login endpoint `/api/auth/login` to `Auth.routes.js` which should be pretty self-explanatory.  

With all that new code added our app is starting to shape up. We currently have a way to register new users, and also a way to validate returning users accounts & passwords. The final piece missing, as I mentioned earlier is some sort of authentication token so our Express REST API can know when it's dealing with an authenticated user vs an unauthenticated one.  

___
### Quick aside on JWT's for API Authentication

Without trying to go into ***too*** much detail about JWTs (JSON Web Tokens) or REST API Authentication methods here, I want to briefly explain what it is we'll be doing to add JWTs to our app, and why I chose them.

Oftentimes, I feel that a lot of developers (especially in tutorials) will use JWTs just because it's latest shiny new JS toy, or because it's JS based Auth token and their writing a tutorial in JS.  

While there tons more developers that choose JWTs (or different tokens) for the right reasons, I think it's beneficial to explain the pros & cons they offer and why I'm using it here.  

JWTs are [cryptographically signed](https://en.wikipedia.org/wiki/Digital_signature) using a secret key that (hopefully) only our app has access to. That means we can generate a JWT for our client, and when they send it back to us, we can verify wether or not the JWT was created by us. 

That also means that we never have to make a call to the database, or even store our client's JWTs in a database, in order for them to be used.

This is both a pro and a con of JWTs. Assume for a minute that a hacker get's ahold of a client's JWT, they can now interact with our app as that compromised user. You might think that a simple solution is to just invalidate that JWT or add it to a `denylist`, but remember, we don't have either of those.  

The only way to _invalidate_ that Token would be to change the secret key our app is signing JWTs with, which would affect **every user and JWT**.  

Since our app is simple and more of a proof-of-concept right now, we're fine using JWTs as long as we're aware of the potential security concerns. Additionally, not having to make a database call to verify a user's authentication status will work well for our current application setup.
___

Let's go ahead and add JWT authentication into our app. Thanks to [Danny Denenberg for a nice guide on simple JWT implementation in Express](https://www.digitalocean.com/community/tutorials/nodejs-jwt-expressjs). We'll need to install two new packages, jsonwebtoken to read and create JWTs and dotenv to store our JWTs secret key in a .env file.

```bash
npm install jsonwebtoken dotenv
```

We are also going to create a new directory in our `/src/` folder, called `utils` to store our JWT related code. Inside the newly create `/src/utils` directory. Create a file `JWTauth.js`.

Finally, in the `/backend` directory (aka the project root), create a new file `.env`. Note, if you put your `.env` file inside `/src/` it won't work and you'll get `undefined` when you try to acccess any env variables.

***/backend/.env***
```js
JWT_SECRET=secret
```

(In a real app you wouldn't want to use "secret" as your JWT secret, you also wouldn't want to publish that anywhere, ie Github, etc.)

Update our `/src/app.js` file to read our new .env file, add the following to lines 4, 5 & 6 of `app.js`

***/src/app.js***
```js
// Dotenv config
const dotenv = require('dotenv');
dotenv.config();
```

Add the following code to the new `/src/utils/JWTAuth.js` file:

***/src/utils/JWTAuth.js***
```js
const jwt = require('jsonwebtoken')

exports.generateAccessToken = function (username) {
    return jwt.sign({uid: username}, process.env.JWT_SECRET, {expiresIn: "2h"})
}

exports.authenticateToken = function (req, res, next) {
    const authHeader = req.headers['authorization']
    const token = authHeader && authHeader.split(' ')[1]

    if(token == null) {
        return res.sendStatus(401)
    }

    jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
        if(err) {
            console.log(err)
            return res.status(403)
        }

        req.user = user
        next()
    })
}
```

Finally, let's update our Register User and Login User functions in the Auth Service to generate JWTs for authenticated users.

Add this on line 5 of `/src/services/Auth.service.js`, it come immediately after the previous `require()` imports.  

***/src/services/Auth.services.js***
```js
const jwtAuth = require('../utils/JWTauth')
```

Now, we can call the `jwtAuth.generateAccessToken()` function inside our Service Layer to get a valid JWT for our client.

First, we'll update the `loginUser` function in Auth Service to generate our JWT.

Update the final 3 lines in the loginUser function, this should start with our placeholder comment `// TODO - JWTs....`, you can remove that comment now.

***/src/services/Auth.services.js - loginUser()***
```js
...
var authToken = await jwtAuth.generateAccessToken(existingUser[0].username)

return {token: authToken}
```

Additionally, update the final 3 lines of our registerUser function in the Auth Service to make a call to loginUser. 

***/src/services/Auth.service.js - regiserUser()***
```js
...
var authToken = await exports.loginUser({"username": newUser.username, "password": newUserInfo.password})

return authToken
```

With that code added, we can now successfully register users, then log them in and return a valid JWT. Existing users can also login with a valid username / password combination, and recieve a new valid JWT.  

We've come along way to building the Auth component of our app and we're ***almost*** done. The final step is to add a new `protected route` that will implement our `authenticateToken()` middleware function we defined in the `JWTauth.js` file.

Open up `/src/routes/Auth.routes.js` and update it so that is looks like the following:  
***/src/routes/Auth.routes.js***
```js
const router = require("express").Router()
const authController = require("../controllers/Auth.controller")
const jwtAuth = require('../utils/JWTauth')

// POST - /api/auth/register
router.post('/register', authController.register)

// POST - /api/auth/login
router.post('/login', authController.login)

// PROTECTED ROUTE - ALL /api/auth/protected
router.all('/protected', jwtAuth.authenticateToken, authController.protected)


module.exports = router;
```

You'll notice that we added a new `ALL` (this just means it will accept any valid HTTP request) endpoint at `/api/auth/protected`, and added two functions after the route declaration. The first function is our `jwtAuth.authenticateToken` which acts as middleware. That means that any request sent  to the `/api/auth/protected` endpoint will first be sent to `jwtAuth.authenticateToken` before being sent to `authController.protected`. We haven't implemented the `protected` function in our `authController` so let's do that now. 

Add the following code to the end of our Auth Controller:

***/src/controllers/Auth.controller.js***
```js
...
exports.protected = async function(req, res) {
    console.log("Reached Protected Route")

    res.send("/protected")
}
```

We should now be able to create a new user, receive a valid JWT, and use that JWT to authenticate and reach our protected endpoint.

Let's start by confirming the endpoint is inaccessible to unauthenticated users.  

Back in Postman, create a new request to the endpoint `/api/auth/protected`. Since we used the router.all() for this endpoint you can make the request a `GET` or a `POST` or whatever else you'd like.

Send the request through, and you should see a response "Unauthorized" with status code 401.  

Next, let's test registering a new user, which will in turn test the login function, by updating the body of our `POST` `/api/auth/register` request to the following:

(since our app checks the username field for existing users, we're updating that here.)
```json
{
    "username": "adam2",
    "password": "adamPass"
}
```

After sending that request through, you should get response similar to the following:
```json
{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJhZGFtMiIsImlhdCI6MTY0NjI0MTI5MiwiZXhwIjoxNjQ2MjQ4NDkyfQ.-vaQXL5KzgeJ4Wer_sdMa-hWmovCezCRxXevEZvurfE"
}
```

If you want to examine the JWT, head on over to [JWT.io](jwt.io) and copy and paste the token value into the editor. Since the secret this token was generated with is just "secret", again that's a TERRIBLE IDEA in production, you should be able to verify the token as well.

With our newly created JWT, let's copy the value, ie just this part:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJhZGFtMiIsImlhdCI6MTY0NjI0MTI5MiwiZXhwIjoxNjQ2MjQ4NDkyfQ.-vaQXL5KzgeJ4Wer_sdMa-hWmovCezCRxXevEZvurfE
```

And then add it to our Postman `/api/auth/protected` request in the `authorization` header. One thing to note about working with JWTs in Auth headers, is that the token itself is usually prefixed by the term "Bearer". So in Postman >> Headers >> type in "Authorization" for the header name then add the following for the value:
```js
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiJhZGFtMiIsImlhdCI6MTY0NjI0MTI5MiwiZXhwIjoxNjQ2MjQ4NDkyfQ.-vaQXL5KzgeJ4Wer_sdMa-hWmovCezCRxXevEZvurfE
```

With that header added, resend the request. If everything goes well, instead of the "Unauthorized" response, you should now see a response body "/protected" which is what we returned in our `authController.protected` function. You'll also notice we should have console logged the line "Reached Protected Route" to our dev console. I added this to demonstrate that the `jwtAuth.authenticateToken` stops further code execution in the case of unauthorized users.

And with that, we have now implemented a Auth system, albeit a simple one, for our application. Since we covered so much ground in this section, I think this would be a good place to take a pause. In the next section, we'll start back up with deploying our newly updated app onto AWS, and test out any issues that might occur in the cloud that we're not running into on our local dev machine.  

I also decided on a new name for our Todo App, "git-er-dern", which has a 2:3 pun to word ratio. Quite impressive in my humble opinion.