/auth-jwt-service

A basic JWT authentication microservice that can spun up and plugged into an existing or new application

Primary LanguageJavaScriptMIT LicenseMIT

Simple AUTH Microservice

The purpose of this project is to have an easily usable authentication microservice that one could "plug and play" into their existing API or gateway. This project is be 100% working out of the box. However, it is also meant to be a starting point for those who want a more customized authentication service. The microservice is also meant to be used how you would like it to be used, meaning certain pages (login, register, password reset) are provided but are by no means required to be used. If you would like to customize the pages or use another service for the pages, it is encouraged.

NOTE: Passwords are hashed using bcrypt

Current Features

  • Simple login page
  • Simple registration page
  • Password reset functionality
  • Delete account
  • Uses MongoDB as datastore
  • Dockerfile and Docker-Compose configuration

Future Plans

  • Encrypt everything in database (User collection/table) at rest
  • Allow for interchangeable databases with just specifying an environment variable (currently only MongoDB will be supported out of the box)
  • Add more security features to prevent against spamming (brute-force) and DOS attacks
  • Verify email
  • Flags for using sessions and bearer tokens

API

All optional fields are marked with '(optional)' in front of them. Do not include '(optional)' in actual request.

Also, note that the message is not always ok, even if the error field is not included

/register - POST

Accepts: application/x-www-form-urlencoded, application/json

  • email: String (the email associated with the user)
  • password: String (the password from the user)
  • (optional) firstname: String
  • (optional) lastname: String
  • (optional) username: String (a non-unique username)

Example request:

{
    "email": "john@smith.com",
    "password": "password",
    "username": "jsmith192"
}

Returns: application/json

  • message: String (ok if everything went alright)
  • error: (only included when error occurred)

/login - POST

Accepts: application/x-www-form-urlencoded, application/json

  • email: String (the email associated with the user)
  • password: String (the password from the user)

Returns: application/json

  • message: String (ok if everything went alright)
  • token: String (Only included when login is successful)
  • user: an object representing the logged in user

Example response:

{
    "message": "ok",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVjMWFkNjQ1MjMwNWJjMTJlY2YyNGE0MiIsImlhdCI6MTU0NTI2MjczMywiZXhwIjoxNTQ1NDM1NTMzfQ.RcJv26xteu-SsdiJ572gb4y7GAJ9Jo9dAlUiwvi7oj8"
}

/authenticate - POST - used to verify token

Accepts: application/x-www-form-urlencoded, application/json, (Authorization header to be added as option instead of the other two)

  • jwtToken: String (the token received from /login)

Returns: application/json

  • message: String

/forgot - POST - used to initiate password reset

Accepts: application/x-www-form-urlencoded, application/json

  • email: String (the user email that is requesting the reset)

Returns: application/json

  • message: String (ok if everything went alright)
  • error: (only included when error occurred)

/reset/:token - POST - performs the password reset operations

:token is the token that was generated by /forgot. The token is already included in the link that is emailed to the user. This is NOT the JWT from /login

Accepts: application/x-www-form-urlencoded, application/json

  • password: String (the new user password)

Returns: application/json

  • message: String (ok if everything went alright)
  • error: (only included when error occurred)

/logout - POST - performs the logout

Currently, logout only deletes the cookie with the token, if cookies are enabled.

Accepts: application/x-www-form-urlencoded, application/json

  • jwtToken: String (the token received from /login) - not needed if cookies enabled

Returns: application/json

  • message: String

/delete-account - DELETE - deletes user account

Accepts: application/x-www-form-urlencoded, application/json

  • jwtToken: String (the token received from /login)
  • email: String (the email of the user that is to be deleted. This email must match that of the user associated with the jwtToken)

Returns: application/json

  • message: String
  • error: (only included when error occurred)

/register - GET

Returns HTML registration page

/login - GET

Returns HTML login page

/forgot - GET

Returns HTML forgot password page

/reset/:token - GET

Returns HTML password reset page

Customization/Configuration

There are a couple locations where you are able to easily change some of the configurations. The .env file contains environment variables that pertain to service configurations and private data. You should modify the variables to your uses.

Currently, the email information is only used for password reset related functionality. However, an optional feature to send a verification email will also be using the email information in a future release. As you might notice, email credentials are in the .env file and email service configuration is in the config.js file. You can find the SMTP information on your providers site or here: https://www.arclab.com/en/kb/email/list-of-smtp-and-pop3-servers-mailserver-list.html

.env

# All variables defined here will not overwrite variables currently defined in environment

# Backend services
HOST='localhost'
PORT=3000
AUTH_PATH='/' # used for the optional pages to direct to correct endpoint location (i.e. '/auth/' or '/auth/jwt/')

TOKEN_SECRET='secret' # Change this to whatever you would like your token secret to be
TOKEN_EXPR='2d' # Token Expiration Length. Eg: 60, "2 days", "10h", "7d". A numeric value is interpreted as a seconds count. 
                # If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms").

COOKIE_SECRET='secret'
COOKIE_EXPR=172800000 # The number of milliseconds that the cookie should last. Unlike the TOKEN_EXPR variable, this must be a number. It is currently set to 2 days

EMAIL_USER='username' # the username for the SMTP/email account you are using for password reset
EMAIL_PASS='password' # password for the account mentioned above

# Database
DB_HOST='localhost'
DB_PORT=27017
DB_NAME='JWTService'

It is recommended that the TOKEN_EXPR and the COOKIE_EXPR are set for the same time period.

config.js

The config.js contains some necessary/optional configurations so that you will not have to go digging through the code to fix some things. You might still want to go digging to customize as you see fit.

const databaseConnectionOptions = {
    useNewUrlParser: true,
    reconnectTries: Number.MAX_VALUE,
    reconnectInterval: 500
}

function databaseConnectionError (error) {
    if (error) {
        console.log('Failed to first attempt to connect to database:', error)
    }
}

// Used to send emails for password reset
var smtpTransport = nodemailer.createTransport({
    host: 'smtp.sendgrid.net', // change host to your email or SMTP host
    port: 587, // unsecure SMTP port for SendGrid. Change to the proper port for your service
    secure: false, // Change if sending securely
    auth: {
        user: process.env.EMAIL_USER,
        pass: process.env.EMAIL_PASS
    }
})

var passwordResetEmail = 'noreply@passwordreset.com' // Your 'from' email for password reset

// Only used when the '-c' (enable cookie use) is passed on service startup 
var cookieOptions = { 
    signed: true, 
    maxAge: process.env.COOKIE_EXPR, 
    httpOnly: true 
}

User Schema

{
    email: {
        type: String,
        unique: true,
        required: true,
        trim: true
    },
    username: {
        type: String,
        unique: true,
        required: false,
        trim: true
    },
    firstname: {
        type: String,
        required: false,
        trim: true
    },
    lastname: {
        type: String,
        required: false,
        trim: true
    },
    password: {
        type: String,
        required: true
    },
    dateCreated: {
        type: Date,
        required: false,
        default: Date.now
    },
    passwordResetToken: String,
    passwordResetExpires: Date,
    verified: {
        type: Boolean,
        default: false
    }
}

Getting Started

Getting started is quite simple using the following steps:

Clone the repository to your desired location:

git clone https://github.com/tmcdo1/auth-jwt-service.git

Install all the required packages:

npm install

Run for development purposes (uses nodemon):

npm start

Run for production purposes:

npm run prod

This does not include database setup which is required to use. Follow the MongoDB installation manual: https://docs.mongodb.com/manual/installation/

If running without Docker, make sure to change the DB_HOST variable in the .env file to localhost or the server where your database is located.

Docker

A Dockerfile is provided for this service, as well as a docker-compose.yml. The Docker-Compose configuration includes a MongoDB container set to use /data/db as a volume.

To run the service, start Docker on your machine. Then, just type docker-compose up and everything will be running properly.

Cookies

If the -c or --cookies option is passed when running the service (add or remove from the start or prod script in the package.json file), a signed cookie will be set to store the user's token on the /login POST endpoint. The cookie will then be used to authenticate the user on the /authenticate POST endpoint.

Ex: node index.js -c

From your perspective, using cookies is an easy way to get started, as you will not have to write any code to store the token for the user.