FeathersJS authentication using client certificate

378 Views Asked by At

I'm trying to create my own authentication strategy that reads the client's PKI certificate within a FeathersJS backend. This is handled in a before hook and based on the documentation hooks are

A hook is transport independent, which means it does not matter if it has been called through HTTP(S) (REST), Socket.io, Primus or any other transport Feathers may support in the future. They are also service agnostic, meaning they can be used with ​any​ service regardless of whether they have a model or not.

This is not a bad idea, however I need the TLS socket structure within the hook to get the user's certificate. Essentially calling: req.socket.getPeerCertificate(). I'm using the passport-client-certificate module and here's the strategy in question:

class ClientCertStrategy extends Strategy {
  constructor (options, verify) {
    if (typeof options === 'function') {
      verify = options
      options = {}
    }
    if (!verify) throw new Error('Client cert authentication strategy requires a verify function')

    super()

    this.name = 'client-cert'
    this._verify = verify
    this._passReqToCallback = options.passReqToCallback
  }

  _verified (err, user) {
    if (err) { return this.error(err) }
    if (!user) { return this.fail() }
    this.success(user)
  }

  authenticate (req, options) {
    // Requests must be authorized
    // (i.e. the certificate must be signed by at least one trusted CA)
    if (!req.socket.authorized) {
      this.fail()
      return
    }

    // This is where it fails! req.socket does not exist
    const clientCert = req.socket.getPeerCertificate()

    if (!clientCert) {
      this.fail()
      // TODO: Failure message
      // this.fail({message: options.badRequestMessage || 'Missing client     certificate'}, 400)
      return
    }

    try {
      if (this._passReqToCallback) {
        this._verify(req, clientCert, this._verified.bind(this))
      } else {
        this._verify(clientCert, this._verified.bind(this))
      }
    } catch (err) {
      return this.error(err)
    }
  }
}

Based on the FeathersJS code, the authenticate function basically makes a new request object from the hook. Is there any way to get the user's certificate earlier and make it available later on when the hook is executed?

1

There are 1 best solutions below

0
On

I wrote an issue and was pointed to the FAQ which ultimately helped me solve this:

https://github.com/feathersjs/authentication/issues/693

https://docs.feathersjs.com/faq/readme.html#how-do-i-access-the-request-object-in-hooks-or-services

I ended up writing a middleware that stuck the certificate into the request params. The request params are copied into the hook which is then passed into the Passport strategy.