Oh geez.. After what seems like a century, I finally found a reference from a reference in some random StackOverflow article pointing to a different problem in a different place.
For anyone else heading this way (and using 'pure' JS):
import { v4 as uuidv4 } from 'uuid';
import jwt from 'jsonwebtoken';
import { X509Certificate } from 'crypto';
// load public cert and then the key
// (you can create private key if you use encrypted pk here as well
const newCert = new X509Certificate(readFileSync('./NewCert.pem'));
const keyPEM = readFileSync('./NewCert.key');
// helper function to convert fingerprint to proper encoding
function hexToBase64(hexString) {
// Ensure even number of hex digits
if (hexString.length % 2 !== 0) {
hexString = "0" + hexString;
}
// Convert hex to bytes
const bytes = new Uint8Array(hexString.length / 2);
for (let i = 0; i < hexString.length; i += 2) {
bytes[i / 2] = parseInt(hexString.slice(i, i + 2), 16);
}
// Convert bytes to base64
const base64String = btoa(String.fromCharCode(...bytes));
return base64String;
}
const getJWT = () => {
// You have to get the hex value of the fingerprint
// convert each pair to base-16
// mash back together and base64 it
const fingerprint = hexToBase64(newCert.fingerprint.replace(/:/g, ''));
// create and sign JWT
const JWT = jwt.sign(
{},
{ key: keyPEM }, // can include the passphrase here if using encrypted pk
{
issuer: "<CLIENT_ID>",
subject: "<CLIENT_ID>",
expiresIn: 60 * 60, // time in seconds - 1h in this case
notBefore: 0,
jwtid: uuidv4(),
audience: "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token",
algorithm: 'RS256',
header: {
x5t: fingerprint,
}
});
return JWT;
}