So I previously blogged about replicating Meteor's password hashing, so that migration off of meteor could be (somewhat) painless. While I had it working for JWT logins with Persona in my code, I couldn't get it working with basic auth.

I have some paths that serve up static files (swagger documentation and so forth) for internal consumption only, so I have a middleware that checks for the existence of a flag indicating if a user is an 'internal' user or not, and then I also attached basic auth to the routes for these things. Unfortunately, due to the double hashing we need to use to validate a meteor-ized password, basic auth just didn't want to work.

As AdonisJS is awesome, and uses dependency injection, I knew it had to be possible to alter the authentication scheme being used in a not terribly difficult manner.

Ultimately it is not terribly difficult, but it's also not very well documented, and thus it was very difficult finding the not difficult solution.

Initially I thought, hey I'll just extend the basic auth scheme, and change what gets passed to the password validation method. That worked, but a brief interaction with Virk, Adonis' creator, on discord led me in a better direction...

Extend the default lucid serializer instead of the basic auth scheme. This way, the same password logic can be used for all auth methods.

So, first I added a custom serializer in app/Serializers called MeteorHashSerializer.js:

'use strict'

const LucidSerializer = require('@adonisjs/auth/src/Serializers/Lucid')
const Crypto = require('crypto')

class MeteorHashSerializer extends LucidSerializer {
  async validateCredentails (user, password) {
    if (!user || !user[this._config.password]) {
      return false
    }

    return this.Hash.verify(
      Crypto.Hash('sha256').update(password).digest('hex'),
      user[this._config.password]
    )
  }
}

module.exports = MeteorHashSerializer

That simply overrides the validateCredentials() method of the default lucid serializer, and alters out hash verify logic. Pretty easy so far...

Next, we add a hook to our start/hooks.js file, thusly:

const { ioc } = require('@adonisjs/fold')
const { hooks } = require('@adonisjs/ignitor')

const MeteorHashSerializer = require('../app/Serializers/MeteorHashSerializer')

hooks.before.providersRegistered(() => {
  ioc.extend('Adonis/Src/Auth', 'meteorhash', (app) => {
    const Hash = use('Hash')
    return new MeteorHashSerializer(Hash)
  }, 'serializer')
})

This extends the auth provider, attaching our new custom serializer to it.

Finally, we update the config/auth.js with the new serializer. You can see in our hook that we called it meteorhash:

  basic: {
    serializer: 'meteorhash',
    model: 'App/Models/User',
    scheme: 'basic',
    uid: 'email',
    password: 'password'
  },

It was challenging to get to this point. I love this framework, but some of the gaping holes in the documentation can be frustrating.