I am trying to build a CAC authentication system using node.js but am having trouble. I followed a few tutorials regarding setting up a https server and I can get that to work just fine; however, using the logic below, whenever I access my server:
https://localhost:3000
I am prompted to login with the auth button that is sent from the "/" route handler; however, it immediately defaults to the else clause:
res.status(401)
.send(`Sorry, but you need to provide a client certificate to continue.`)
Normally, when accessing a cac-enabled site, I a prompted to choose a smart card certificate, which allows me to login; however, in this case, I am not being prompted at all.
I found a similar question on SO but was not able to get anything to work from the article they referenced. They mentioned that I would have to set my 'ca' property on the https server to allow the specific CA of the CAC in question. I used my browser to locate my CAC certificates and exported my certificates and put them in the approved CA list but I still do not get any prompt.
I was initially using req.connection.getPeerCertificate()
to prompt for certificates; however, I read that req.connection was deprecated in my verison of Node.JS (14.15.0) so I have also tried req.socket.getPeerCertificate()
. The server seems to run just fine either way and I get no errors client-side or server-side (with exception to the 401 response that is sent from the server since it is not getting a valid certificate). Any and all insight would be greatly appreciated.
const fs = require('fs')
const express = require('express')
const https = require('https')
// Https options
const options = {
// Path to private key (created by openssl in createSelfSignedCert.bat)
key: fs.readFileSync('server_key.pem')
// Path to public key
,cert: fs.readFileSync('server_cert.pem')
// Indicate that https server should request client certificates
,requestCert: true
// Manually handle bad requests (no certs)
,rejectUnauthorized: false
//List of accepted/valid CA certs - just our own for now
,ca: [
fs.readFileSync('server_cert.pem')
,fs.readFileSync('cac52.cer')
,fs.readFileSync('cac_export.p7b')
]
//,ca: .
}
// Use express for routing
const app = express()
// Unprotected public endpoint
app.get('/', (req, res) => {
res.send('<a href="login">Auth</a>')
})
// Protected endpoint
app.get('/login', (req, res) => {
// req.connection is deprecated, perhaps req.socket.getPeerCertificate()
const cert = req.connection.getPeerCertificate()
//const cert = req.socket.getPeerCertificate()
if (req.client.authorized) {
res.send(`Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`)
console.log(`${cert}`)
} else if (cert.subject) {
res.status(403)
.send(`Sorry ${cert.subject.CN}, certificates from ${cert.issuer.CN} are not welcome here.`)
console.log(`${cert}`)
} else {
res.status(401)
.send(`Sorry, but you need to provide a client certificate to continue.`)
}
})
// Create https server with options and app routes
https.createServer(options, app).listen(3000)