DEV Community

Cover image for Simple auth server to authentication and create user
Marcelo
Marcelo

Posted on • Edited on

Simple auth server to authentication and create user

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

Check the Github repository to install and run:
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'
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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

const app = express()
const jwtSecretKey = process.env.JWT_SECRET_KEY
Enter fullscreen mode Exit fullscreen mode

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

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

Set up CORS and JSON middlewares:

app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
Enter fullscreen mode Exit fullscreen mode

Basic home route for the API:

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

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 })
        })
    }
})
Enter fullscreen mode Exit fullscreen mode

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" })
    }
})
Enter fullscreen mode Exit fullscreen mode

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
    })
})
Enter fullscreen mode Exit fullscreen mode

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
    });
})
Enter fullscreen mode Exit fullscreen mode

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')
})
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
pebueno profile image
pebueno

Amazing tutorial! Thank you for sharing, I need to learn JWT once and for all and you made it look easy, will implement it in my next React project.