Simple auth server to authentication and create user
Tags: node, coding, api, jsonwebtoken
Published: 2024-04-20 20:03:42

## Learning about authentication on Nodejs server I made this project to learn and explore.

_Check the Github repository to install and run_:
[auth-server](https://github.com/Marcel-MSC/auth-server)

Libraries that a use:
1. bcryptjs
2. cors
3. dotenv
4. express
5. jsonwebtoken
6. lowdb

### End points: 
- /auth -> The auth endpoint that creates a new user record or logs a user based on an existing record
- /verify -> The verify endpoint that checks if a given JWT token is valid
- /check-account -> An endpoint to see if there's an existing account for a given email address

### Explain the app.js

ES6 stand imports. 
We have the imports to use:

```
import express from "express"
import bcrypt from 'bcryptjs'
import cors from 'cors'
import jwt from "jsonwebtoken"
import { JSONFilePreset } from 'lowdb/node'
import 'dotenv/config'
```

Initialization of the lowdb, it's basically a JSON file:

```
const defaultData = { users: [] }
let db
async function startLowdb() {
    db = await JSONFilePreset('datatable.json', defaultData)
}
startLowdb()
```

Initialize Express app and Define a JWT secret key using .env file:

```
const app = express()
const jwtSecretKey = process.env.JWT_SECRET_KEY
```

A little about CORS:

> Cross-origin resource sharing (CORS) is a mechanism that allows a web page to access restricted resources from a server on a domain different than the domain that served the web page. 
[Cors Wikipedia](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)

Middleware:

> Middleware is a type of computer software program that provides services to software applications beyond those available from the operating system. It can be described as "software glue".
[Middleware cors](https://en.wikipedia.org/wiki/Middleware)

Set up CORS and JSON middlewares: 

```
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
```

Basic home route for the API:

```
app.get("/", (_req, res) => {
    res.send("Auth API.\nPlease use POST /auth & POST /verify for authentication");
});
```

Here things get a little more complex.
The auth endpoint that creates a new user record or logs a user based on an existing record.
Don't forget to pass email and password on the post auth.
If authentication success will return a token.

```
app.post("/auth", (req, res) => {
    const { email, password } = req.body
    // Look up the user entry in the database
    const { users } = db.data
    const user = users.filter(user => email === user.email)
    // If found, compare the hashed passwords and generate the JWT token for the user
    if (user.length === 1) {
        bcrypt.compare(password, user[0].password, function (_err, result) {
            console.log('_err', _err)
            console.log('compare result', result)
            if (!result) {
                console.log('invalid password')
                return res.status(401).json({ message: "Invalid password" });
            } else {
                let loginData = {
                    email,
                    signInTime: Date.now(),
                }
                const token = jwt.sign(loginData, jwtSecretKey)
                res.status(200).json({ message: "authentication success", token })
            }
        })
        // If no user is found, hash the given password and create a new entry in the auth db with the email and hashed password
    } else if (user.length === 0) {
        bcrypt.hash(password, 10, async function (_err, hash) {
            console.log('email e password', { email, password: hash })
            const { users } = db.data
            // db.get("users").push({ email, password: hash }).write()
            db.data.users.push({ email, password: hash })
            await db.write()
            console.log('autorizado com sucesso')
            let loginData = {
                email,
                signInTime: Date.now(),
            }
            const token = jwt.sign(loginData, jwtSecretKey)
            res.status(200).json({ message: "success", token })
        })
    }
})
```

When using auth endpoint, you can get a token to be verified.
The verify endpoint that checks if a given JWT token is valid: 

```
app.post('/verify', (req, res) => {
    const authToken = req.headers.tokenheaderkey;
    console.log('req', req.headers)
    try {
        const verified = jwt.verify(authToken, jwtSecretKey)
        return verified ? res.status(200).json({ status: "logged in", message: "verify with success" }) : res.status(401).json({ status: "invalid auth", message: "error" });
    } catch (error) {
        // Access Denied
        return res.status(401).json({ status: "invalid auth", message: "error" })
    }
})
```

An endpoint to see if there's an existing account for a given email address:

```
app.post('/check-account', (req, res) => {
    const { email } = req.body
    console.log(req.body)
    const { users } = db.data
    const user = users.filter(user => email === user.email)
    console.log(user)
    res.status(200).json({
        status: user.length === 1 ? "User exists" : "User does not exist", userExists: user.length === 1
    })
})
```

An endpoint to delete a user.
Lowdb don't give a delete/remove function.
Using filer, if the user exists and then update the Users' database.json object with new object:

```
app.post('/remove-user', async (req, res) => {
    const { email } = req.body
    const { users } = db.data;
    const user = users.filter(user => email === user.email);
    if (user) {
        const newObj = {};
        newObj.users = [];
        const newUsers = users.filter(userDB => email == userDB.email ? null : userDB);
        newObj.users = newUsers;
        db.data.users = newUsers;
        db.write();
    }
    res.status(200).json({
        status: user.length === 1 ? "User removed" : "User not founded", userExists: user.length === 1
    });
})
```

Some application use process.env to set up the port.
You can use what ever port that is free:

```
app.listen(3080, function () {
    console.log('listen to port 3080')
})
```

It's a simple way to understand how to create a simple backend server for authentication and to control users flow on your app. You can check if the user that is logging and if he still has a valid token.

Of course that more features are needed in a real application.
Some examples to implementation: 
connect to another DB, create others validation to user creation, update and delete.
Put a timer on the token to expire, and you can validate if the user's request has a token that is still valid.

Maybe in the future I will some add more features.

Feel free to clone, fork, issue or a pull request.

Any suggestion are welcome.