Mi intento en javascript
Hice mi servidor internamente, con una ruta post a /register para obtener el token mandando solo el X-Client-Id ya que el secreto lo tendría el servidor.
Para esto usé el token de JsonWebToken. Que facilita mucho este trabajo
curl localhost:5000/register -X "POST" -H "X-Client-Id: 1" | jq

El token también se podría obtener en https://jwt.io/
Código de mi servidor de autenticación (registro)
app.post('/register', (req, res) => {
const uid = req.headers['x-client-id'] || '1';
// Creamos el token con JsonWebToken
const token = jwt.sign(uid, secret);
// Retornamos el token
res.json({ token });
})
Código de mi servidor de autenticación (verificacion de token)
app.get('/login', (req, res) => {
// Obtiene el token de los headers
const token = req.headers['x-token'] || '';
// De esta forma evitamos errores de verificación
try {
// Obtenemos el id verificando que el hash coincida con el secret
const uid = jwt.verify(token, secret);
// Verificamos si el id obtenido es igual a 1
if(uid === "1") {
// Devuelve verdadero si coincide
res.send({ auth: true })
} else {
// Devuelve falso si no coincide
res.send({ auth: false })
}
} catch(e) {
// Devuelve falso si hay un error
res.send({ auth: false })
}
})
Y al final agrego otra excepción a mi middleware de autenticación. Para así poder usar HMAC, HTTP y TOKEN
const bookAuthenticator = async (req, res, next) => {
// Obtiene la autenticación como "username:password"
// Y lo convierte en un array como ["username", "password"]
let authorization = [];
// Verificamos si se envian headers de autenticación HTTP
if(req.headers['authorization']) {
// req.headers.authorization es encodeado en base64, tal como: "Basic bHVjbmVvbmN0OjEyMzQ="
// Con un Buffer lo decodeamos: Buffer.from('string','base64')
authorization = Buffer
// Separamos en dos arreglos, para usar solo el string encodeado
// ["Basic", "bHVjbmVvbmN0OjEyMzQ="]
.from(req.headers.authorization.split(" ")[1], 'base64')
// Lo transformamos en utf-8
// "lucneonct:1234"
.toString('utf-8')
// Lo separamos por el ":" en un array
// ["lucneonct", "1234"]
.split(':');
}
// Verificamos si se envian headers de autenticación HMAC
else if(req.headers['x-hash']) {
// Recibe los parametros enviados en req.headers
// Si no hay se lo deja en blanco
const hash = req.headers['x-hash'] || '';
const uid = req.headers['x-uid'] || '';
const timestamp = req.headers['x-timestamp'] || '';
// Usamos el paquete de crypto que viene con NodeJS para crear el SHA1
const hmac = crypto.createHmac('sha1', secret);
// Pasamos nuestro UID y TIMESTAMP al método update
hmac.update( uid + timestamp );
// Comparamos que el hash enviado y el creado sean iguales
// Usamos hmac.digest('hex') para que nos muestre el hash en hexadecimal
if(hash === hmac.digest('hex')) {
// Rellenamos nuestro arreglo de autorización para pasar la validación posterior
authorization = ['lucneonct', '1234'];
}
}
// Verificamos si se envian headers de autenticación TOKEN
else if(req.headers['x-token']) {
await axios({
method: 'GET',
url: 'localhost:5000/login',
headers: {
"x-token": req.headers['x-token']
}
})
.then(response => {
if(response.data.auth) {
authorization = ['lucneonct','1234'];
}
});
}
// Obtiene username del primer indice del arreglo y la contraseña del segundo
const authUser = authorization[0];
const authPassword = authorization[1];
// Verificamos si los datos no coinciden con nuestro usuario hardcodeado
if( authUser !== "lucneonct" || authPassword !== "1234") {
// Rompe la cadena y retorna un 403 Forbidden
return res.status(403).json({ status: 'Forbidden: missing or invalid auth' });
}
next()
}