I tried different ways to get an Azure AD token using a pfx certificate. But the code below is persistently returning this error: Error: secretOrPrivateKey must be an asymmetric key when using RS256
I also tried using @azure/keyvault-certificates and azure/identity, then genrating .pem files with open ssl, and in that case I also get the same error.
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const https = require('https');
const qs = require('querystring');
const jwt = require('jsonwebtoken');
const certPath = path.join(__dirname, 'certificate.pfx');
const certBuffer = fs.readFileSync(certPath);
const tokenEndpoint = 'https://login.microsoftonline.com/my-tenant-id/oauth2/token';
const clientId = 'my-app-id';
const tokenPayload = {
aud: tokenEndpoint,
iss: clientId,
sub: clientId,
jti: Math.random().toString(36).substring(7),
nbf: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600,
};
const tokenOptions = {
algorithm: 'RS256',
};
const clientAssertion = jwt.sign(tokenPayload, certBuffer, tokenOptions);
console.log('Client Assertion:', clientAssertion);
const postData = {
grant_type: 'client_credentials',
client_id: clientId,
resource: 'resource',
client_assertion: clientAssertion,
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
};
const axiosConfig = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
httpsAgent: new https.Agent({
pfx: certBuffer,
passphrase: '',
rejectUnauthorized: true,
}),
};
async function getToken() {
try {
const response = await axios.post(tokenEndpoint, qs.stringify(postData), axiosConfig);
const token = response.data.access_token;
console.log('Token:', token);
return token;
} catch (error) {
console.error('Error:', error.message);
if (error.response) {
console.error('Error response from Azure AD:', error.response.data);
}
throw error;
}
}
getToken().then(token => {
console.log('Token:', token);
}).catch(err => {
console.error('Failed to obtain token:', err);
});
In my case, I ran below OpenSSL commands to create private key and certificate like this:
openssl genrsa -out sridemo.pem 2048
openssl req -new -key sridemo.pem -out sridemo.csr
openssl x509 -req -days 365 -in sridemo.csr -signkey sridemo.pem -out sridemo.crt
Response:
Make sure to upload sridemo.crt
file to your Entra ID app registration and note thumbprint value:
To generate Microsoft Entra token using certificate, I used @azure/msal-node with below sample code:
const fs = require('fs');
const msal = require('@azure/msal-node');
const tenantId = 'tenant_id';
const clientId = 'appId';
const authority = `https://login.microsoftonline.com/${tenantId}`;
const certPath = 'path/to/your/certificate.pem';
const certThumbprint = 'cert_thumbprint';
const scope = 'https://graph.microsoft.com/.default';
// Read the certificate
const privateKey = fs.readFileSync(certPath, 'utf8');
const confidentialClientConfig = {
auth: {
clientId: clientId,
authority: authority,
clientCertificate: {
thumbprint: certThumbprint,
privateKey: privateKey,
},
},
};
const cca = new msal.ConfidentialClientApplication(confidentialClientConfig);
// Acquire a token
async function getToken() {
try {
const tokenResponse = await cca.acquireTokenByClientCredential({
scopes: [scope],
});
console.log('Access Token:', tokenResponse.accessToken);
} catch (error) {
console.error('Error acquiring token:', error);
}
}
getToken();
Response:
To confirm that, you can decode the token in jwt.ms and check claims as below:
Reference:
MSAL NODE · AzureAD/microsoft-authentication-library-for-js (github.com)