Basic Authentication With AdonisJs

With the release of Adonis 2.0, i have received lot of queries on how to authenticate requests in Adonis.

Adonis is an MVC framework for NodeJs, it borrows the concept of Service providers from popular PHP Framework Laravel.

I personally divides authentications into two different parts. First the stateful authentication where we save cookies into browser and use it to find login state for a given user. Other one is the stateless authentication, where each request is authenticated.

The later one is quite popular with RestFul API’s, where we authenticate every API request and a common way to achieve that is to make use of Basic Auth.

In this article, i will share the process of writing a middleware and then authenticating routes with the help of basicauth middleware. Also the final code is available on https://github.com/thetutlage/adonis-auth-tutorial.

 Creating a new application

After following the installation process run the below command to create a new application.

adonis new basic-auth

 Laying down application requirements

It is important to understand the application requirements before writing the first line of code. It helps in setting up the expectations and results in better management of code.

  1. We need a users table to store users.
  2. User model to persist and fetch users.
  3. A middleware to authenticate requests.

As we are clear with our expectations, let’s start by achieving these requirements one by one.

 Creating users table using migrations

Adonis projects have ace commands to do handful of tasks from your command line, and one of them is to make migrations.

./ace migration:make create_users_table --create="users"

Above command will setup the migration file inside migrations directory. Open that file and add some fields to it.

'use strict'

const Schema = use('Schema')

class Users extends Schema {

  up () {
    this.create('users', function (table) {
      table.increments('id')
      table.string('username', 60).unique()
      table.string('password', 80)
      table.timestamps()
      table.timestamp('deleted_at')
    })
  }

  down () {
    this.drop('users')
  }
}

module.exports = Users

Next in order to create the table using the above schema, you need to run the below command.

./ace migration:run

If you are following along with me, you are likely going to get an error that says sqlite3 is not installed.

Knex: run
$ npm install sqlite3 — save
Error: Cannot find module 'sqlite3'

Newly created Adonis application is configured to use sqlite as the default database. You can change this from your .env file, or read more about Database connection setup

In order to solve the above error, let’s install sqlite as a development dependency

npm install --save-dev sqlite3

Now re-running the migrations command will setup the database and users table for you. We are done with the first step of our requirements and here is the commit for that https://github.com/thetutlage/adonis-auth-tutorial/commit/ccb2edb5b8868841f289fe5ddfbc0d3bbcb39d9e

 Setting up User model

Models are data stores, which talks directly to your database and each model generally represents a database table.

./ace make:model User

Above command will create a new file inside app/Model directory. There is no need to change the file or make any modifications. Commit for the above change https://github.com/thetutlage/adonis-auth-tutorial/commit/070f6c2acf0973764c1ab0172ca64c174eebdd57

 Authentication Middleware

Finally we have reached the stage of writing the middleware for basic authentication.

According to basic authentication specs, one should look for Authorization header inside request headers and use that information to authenticate a given user.

./ace make:middleware BasicAuth

Above command will generate a middleware file inside app/Http/Middleware folder.

Here we will write our logic to authenticate the credentials. If we want, we can read the credentials manually from the headers and base64 decode them or we can use a npm module to do that for us. https://www.npmjs.com/package/basic-auth

npm i --save basic-auth

As I mentioned, it is always good to lay down steps before implementing a feature. Here we will write all the steps we need to perform inside our BasicAuth middleware.

  1. Get credentials from request headers.
  2. Validate credentials with database records.

 Get credentials from request headers

Below code will go under your newly created middleware file. app/Http/Middleware/BasicAuth.js

'use strict'

const auth = use('basic-auth')

class BasicAuth {

  * handle (request, response, next) {
    const credentials = auth(request.request)

    if (!credentials) {
      response.status(401).send({
        error: 'Please enter your account credentials'
      })
      return
    }

  }

}

module.exports = BasicAuth

 Validate credentials with database records

Now we need to make sure the credentials entered by end-users are valid and does exists in our database. For that we will make use of User Model.

 Grabbing user model

use the User model on top of your middleware file just after the basic-auth import.

const User = use('App/Model/User')

 Querying users with username

After the credentials check, we need to find the user with the provided username inside our database.

    // CHECKING FOR USER INSIDE USERS TABLE
    const user = yield User
      .where('username', credentials.name)
      .first()
      .fetch()

    if (!user.size()) {
      response.status(401).send({
        error: 'Please enter your account credentials'
      })
      return
    }

 What about the password check ?

Here i will assume that you are saving users in your users table with their passwords hashed. Which means records saved does not have plain passwords and we cannot query them with a .where clause.

[Hash provider]((http://adonisjs.com/docs/2.0/hashing)) in Adonis makes it super simple to verify hashed passwords with plain passwords and login flow is the best use case of using Hash provider.

 Using Hash provider

const Hash = use('Hash')

 Verifying passwords

Finally we can access the plain value from the credentials object of Authorization header and the hash value is stored under database and verify them with each other.

    const userPassword = user.get('password')
    const result = yield Hash.verify(credentials.pass, userPassword)

    if (!result) {
      response.status(401).send({
        error: 'Password mismatch'
      })
      return
    }

    // it is important to yield next if authentication is successful.
    yield next

And you are done. So we have got a middleware which can do all the required authentication stuff using Basic authentication specs. Here is the commit for the changes so far. https://github.com/thetutlage/adonis-auth-tutorial/commit/5e18dc4a170532ceabbb743378705069e17770f9

 Wait

You created the middleware, but you are required to register it before you can really make use of it.

In app/Http/kernel.js file under namedMiddleware you need to register it.

const namedMiddleware = {
  'auth.basic': 'App/Http/Middleware/BasicAuth'
}

From here you are good to go and make use of auth.basic middleware in your routes.

Route
  .get('/users', 'UsersController.index')
  .middlewares(['auth.basic'])

Commit for above change. https://github.com/thetutlage/adonis-auth-tutorial/commit/d8f5c6acbfab453dabd1fba4358833e7cc569b25

 Known issues.

Below is the list of known issues you are likely going to face. If you found any other, please leave comments and i will update the post.

 Cannot find module Hash

If you are getting this error, it means you did not paid attention to the Hash Provider Setup. Now please go ahead and read how to set it up.

 
128
Kudos
 
128
Kudos

Now read this

Contributing to AdonisJs

Quite often I am asked on how to contribute to AdonisJs and it is so important to have some clarity on same since contributors are the force behind any open source project. This post gives an overview on how you can get started... Continue →