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?
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 requestparams
are copied into the hook which is then passed into the Passport strategy.