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.
- We need a users table to store users.
- User model to persist and fetch users.
- 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.
- Get credentials from request headers.
- 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.