I'm working on a Node.js application that uses Microsoft Authentication Library (MSAL) to authenticate with Microsoft Graph API using OAuth 2.0.
Currently, authentication is handled using a client secret, but the goal is to improve security by switching to a client certificate instead.
Here’s the current implementation for acquiring an access token with the clientSecret:
const config = {
auth: {
clientId: clientId,
authority: `https://login.microsoftonline.com/${tenantId}`,
clientSecret: clientSecret,
}
};
const cca = new msal.ConfidentialClientApplication(config);
const request = {
scopes: ['https://graph.microsoft.com/.default'],
};
async function getAccessToken() {
try {
const response = await cca.acquireTokenByClientCredential(request);
return response.accessToken;
} catch (error) {
console.error(`Error acquiring token: ${error}`);
}
}
Additionally, this function is used to fetch a list of users from Microsoft Graph API:
async function listUsers() {
const token = await getAccessToken();
if (!token) {
console.error('Failed to acquire access token');
return;
}
const options = {
headers: {
'Authorization': `Bearer ${token}`
}
};
try {
const response = await axios.get('https://graph.microsoft.com/v1.0/users', options);
console.log(response.data);
} catch (error) {
console.error(`Error fetching users: ${error}`);
}
}
The goal now is to replace the clientSecret with a client certificate to enhance security. What changes are needed to modify the MSAL configuration for using client certificate instead?
In my case, I used below commands to create private key and certificate:
openssl genrsa -out private.key 2048
openssl req -new -key private.key -out request.csr
openssl x509 -req -in request.csr -signkey private.key -out certificate.crt -days 365
Get-Content private.key, certificate.crt | Set-Content certificate.pem
Now, make sure to upload certificate.crt
file in your Entra ID app registration like this:
When I ran below modified code, I got the list of users successfully in my Node JS application:
const msal = require('@azure/msal-node');
const fs = require('fs');
const clientId = 'appId';
const tenantId = 'tenantId';
const certificatePath = './certificate.pem';
const certificate = fs.readFileSync(certificatePath, 'utf8');
const config = {
auth: {
clientId: clientId,
authority: `https://login.microsoftonline.com/${tenantId}`,
clientCertificate: {
thumbprint: '5AA5151C05CC022xxxxxxxxxxxxx',
privateKey: certificate,
},
},
};
const cca = new msal.ConfidentialClientApplication(config);
const request = {
scopes: ['https://graph.microsoft.com/.default'],
};
async function getAccessToken() {
try {
const response = await cca.acquireTokenByClientCredential(request);
return response.accessToken;
} catch (error) {
console.error(`Error acquiring token: ${error}`);
}
}
async function listUsers() {
const token = await getAccessToken();
if (!token) {
console.error('Failed to acquire access token');
return;
}
const axios = require('axios');
const options = {
headers: {
'Authorization': `Bearer ${token}`,
},
};
try {
const response = await axios.get('https://graph.microsoft.com/v1.0/users', options);
console.log(response.data);
} catch (error) {
console.error(`Error fetching users: ${error}`);
}
}
listUsers();
Response: