⚠️ Before you start here, make sure you understand Initialize confidential client applications.
You can build confidential client applications with MSAL Node (web apps, daemon apps etc). A client credential is mandatory for confidential clients. Client credential can be:
managed identity
: this is a certificateless scenario, where trust is established via the Azure infrastructure. No secret / certificate management is required. MSAL does not yet implement this feature, but you may use Azure Identity SDK instead. See https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/clientSecret
: a secret string generated during the app registration, or updated post registration for an existing application. This is not recommended for production.clientCertificate
: a certificate set during the app registration. The certificate needs to have the private key, because it will be used for signing an assertion that MSAL generates. ThethumbprintSha256
is a X.509 SHA-256 thumbprint of the certificate, and theprivateKey
is the PEM encoded private key.clientAssertion
: instead of letting MSAL create an assertion, the app developer takes control. Useful for adding extra claims to the assertion or for using KeyVault for signing, instead of a local certificate. The certificate used to sign the assertion still needs to be set during app registration.
Note: 1p apps may be required to also send x5c
. This is the X.509 certificate chain used in subject name/issuer auth scenarios.
Secrets should never be hardcoded. The dotenv npm package can be used to store secrets or certificates in a .env file (located in project's root directory) that should be included in .gitignore to prevent accidental uploads of the secrets.
Certificates can also be read-in from files via NodeJS's fs module. However, they should never be stored in the project's directory. Production apps should fetch certificates from Azure KeyVault, or other secure key vaults.
Please see certificates and secrets for more information.
See the MSAL sample: auth-code-with-certs
If you do not have a certificate, you can create a self-signed certificate using PowerShell or using Azure KeyVault.
You need to upload your certificate to Azure AD.
- Navigate to Azure portal and select your Azure AD app registration.
- Select Certificates & secrets blade on the left.
- Click on Upload certificate and select the certificate file to upload (e.g. example.crt).
- Click Add. Once the certificate is uploaded, the thumbprint (SHA-256), start date, and expiration values are displayed.
For more information, see: Register your certificate with Microsoft identity platform
const msal = require("@azure/msal-node");
require("dotenv").config(); // process.env now has the values defined in a .env file
const config = {
auth: {
clientId: "YOUR_CLIENT_ID",
authority: "https://#.microsoftonline.com/YOUR_TENANT_ID",
clientCertificate: {
thumbprintSha256: process.env.thumbprint,
privateKey: process.env.privateKey,
},
},
};
// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);
Both thumbprintSha256
and privateKey
are expected to be strings. privateKey
is further expected to be in the following form (PKCS#8):
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDkpKPrsfpIijS3
z2HCpDsa7dxOsKIrm7F1AtGBjyB0yVDjlh/FA7jT5sd2ypBh3FVsZGJudQsLRKfE
// ...
-----END ENCRYPTED PRIVATE KEY-----
:informationsource: Alternatively, your private key may begin with
-----BEGIN PRIVATE KEY-----
(unencrypted _PKCS#8) or-----BEGIN RSA PRIVATE KEY-----
(PKCS#1). These formats are also permissible. The following can be used to convert any compatible key to the PKCS#8 key type:openssl pkcs8 -topk8 -inform PEM -outform PEM -in example.key -out example.key
If you have encrypted your private key (or if your private key is already encrypted) with a pass phrase, you'll need to decrypt it before passing it to MSAL Node.
Important: Never hardcode passwords in source code. Both the certificate private key and the optional descryption password should be fetched from a secure location (e.g. Azure KeyVault) and deployed securely with your web api.
This can be done using Node's crypto module. Use the createPrivateKey()
method to parse and export your key:
const fs = require("fs");
const crypto = require("crypto");
const privateKeySource = fs.readFileSync("<path_to_key>/example.key");
const privateKeyObject = crypto.createPrivateKey({
key: privateKeySource,
passphrase: process.env.YOUR_PASSPHRASE,
format: "pem",
});
const privateKey = privateKeyObject.export({
format: "pem",
type: "pkcs8",
});
OpenSSL can be used for converting pfx encoded certificate files to pem:
openssl pkcs12 -in certificate.pfx -out certificate.pem
If the conversion needs to happen programmatically, then you may have to rely on a 3rd party package, as Node.js offers no native method for this. For instance, using a popular TLS implementation like node-forge, you can do:
const forge = require("node-forge");
/**
* @param {string} pfx: certificate + private key combination in pfx format
* @param {string} passphrase: passphrase used to encrypt pfx file
* @returns {Object}
*/
function convertPFX(pfx, passphrase = null) {
const asn = forge.asn1.fromDer(forge.util.decode64(pfx));
const p12 = forge.pkcs12.pkcs12FromAsn1(asn, true, passphrase);
// Retrieve key data
const keyData = p12
.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag })
[forge.pki.oids.pkcs8ShroudedKeyBag].concat(
p12.getBags({ bagType: forge.pki.oids.keyBag })[
forge.pki.oids.keyBag
]
);
// Retrieve certificate data
const certBags = p12.getBags({ bagType: forge.pki.oids.certBag })[
forge.pki.oids.certBag
];
const certificate = forge.pki.certificateToPem(certBags[0].cert);
// Convert a Forge private key to an ASN.1 RSAPrivateKey
const rsaPrivateKey = forge.pki.privateKeyToAsn1(keyData[0].key);
// Wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
// Convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
const privateKey = forge.pki.privateKeyInfoToPem(privateKeyInfo);
console.log("Converted certificate: \n", certificate);
console.log("Converted key: \n", privateKey);
return {
certificate: certificate,
key: privateKey,
};
}
The OAuth 2.0 protocol recommends using an HTTPS connection whenever possible. Most cloud services like Azure App Service will provide HTTPS connection by default via proxy. If for testing purposes you would like to setup your own HTTPS server, see the Node.js HTTPS guide.
You'll also need to add your self-signed certificates to the credential manager / key chain of your OS to bypass the browser's security policy. You may still see a warning in your browser afterwards (e.g. Chrome).
-
For Windows users, follow the guide here: How to: View certificates with the MMC snap-in.
-
For Linux and MacOS users, please consult your operating system documentation on how to install certificates.
⚠️ You might need administrator privileges for running the commands above.
In some cases, you may receive an error from Azure AD when trying to authenticate using certificates, such as the AADSTS700027: Client assertion contains an invalid signature
error, indicating that the certificates and/or private keys that you use to initialize MSAL Node are malformed. A common reason for this is that the certificate / private key string that you are supplying to MSAL Node contains unexpected characters, such as return carriages (\r
) or newlines (\n
):
-----BEGIN CERTIFICATE-----\nMIIDDzCCAfegAwIBAgIJAMkyzQVK88NHMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMQ4wDAYDVQQHEwVLaXN0YTEQMA4G0fbkqbKulrchGbNgkankZtEVg4PGjobZq7B+njvcVa7SsWF/WLq5AUbw==\r\n-----END CERTIFICATE-----
Alternatively, your certificate / key file may contain bag attributes:
Bag Attributes
localKeyID: 28 B5 8E 16 11 88 E9 00 58 D5 76 30 12 B9 59 B8 E4 CE 7C AA
subject=/C=UK/ST=Suffolk/L=Ipswich/O=Example plc/CN=alice
issuer=/C=UK/ST=Suffolk/L=Ipswich/O=Example plc/CN=Certificate Authority/emailAddress=ca@example.com\n
-----BEGIN CERTIFICATE-----
MIIDDzCCAfegAwIBAgIJAMkyzQVK88NHMA0GCSqGSIb3DQEBBQUAMIGCMQswCQYD
VQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMQ4wDAYDVQQHEwVLaXN0YTEQMA4G
0fbkqbKulrchGbNgkankZtEVg4PGjo+Y8MdMjtfSZB29hwYvfMX09jzJ68ZqmpYQ
njvcVtLbEZN5OGCkaslb/f2OxLbsUNgIbws538WnaaufDvKmQe2kUdWmpl9Wn9Bf
bZq7B+njvcVa7SsWF/WLq5AUbw==
-----END CERTIFICATE-----
In such cases, you are responsible for cleaning the string before you pass them to MSAL Node configuration. For instance:
const msal = require("@azure/msal-node");
const fs = require("fs");
const privateKeySource = fs.readFileSync("<path_to_key>/certs/example.key");
const privateKey = Buffer.from(privateKeySource, "base64")
.toString()
.replace(/\r/g, "")
.replace(/\n/g, "");
const config = {
auth: {
clientId: "YOUR_CLIENT_ID",
authority: "https://#.microsoftonline.com/YOUR_TENANT_ID",
clientCertificate: {
thumbprintSha256: process.env.thumbprint,
privateKey: privateKey,
},
},
};
// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);