access SSL certificate in Azure Web App - node.js

I've uploaded an SSL certificate to my Azure Web App, running on Node, and now I'd like to access my certificate programmatically from my Node scripts to use it for signing JWTs. Is there a way to do this?
I've found similar answers for C#, but I haven't been able to translate this into Node-world.
Update
Here is code that worked successfully, with help from #peter-pan-msft. Before running this code, I had to SFTP upload my SSL certificate to a private folder on the server.
process.env.KEY = fs.readFileSync('path-to-private-folder/mykey.pem');
const jwt = require('jsonwebtoken');
const token = jwt.sign(payload, process.env.KEY, opts);

There are two samples separately from AzureAD on GitHub and azure-mgmt on npmjs.org.
https://github.com/AzureAD/azure-activedirectory-library-for-nodejs/blob/master/sample/certificate-credentials-sample.js
https://www.npmjs.com/package/azure-mgmt
If you want to use the certification for doing Azure Management, you can directly refer to the second sample.

Related

Replacing secret with certificate in Azure app registration

I am currently using a client secret with an Azure app registration to access an Azure Media service from an App Service. I want to replace the client secret with a certificate as the certificate will last longer. I have successfully generated a certificate and uploaded it to the app registration.
Using the client secret seems straight forward. I create environment variables (in the app service configuration or local.settings.json) for the app registration client ID, app registration client secret and tenant ID and then use the following code:
private async Task<ServiceClientCredentials> GetCredentialsAsync(string aadClientId, string aadSecret, string aadTenantId)
{
ClientCredential clientCredential = new ClientCredential(aadClientId, aadSecret);
return await ApplicationTokenProvider.LoginSilentAsync(aadTenantId, clientCredential,
ActiveDirectoryServiceSettings.Azure);
}
How do I change this code to use the certificate?
I tried to reproduce the same in my environment and got the results like below:
I created an Azure AD Application and uploaded a certificate:
To generate the access token using certificate, you can declare the below parameters in your app.settings file:
"AzureAd": {
"Scope":"https://graph.microsoft/.default",
"Instance":"https://login.microsoftonline.com/",
"Domain":"XXX.onmicrosoft.com",
"TenantId":"YourTenantID",
"ClientId":"ClientID",
"ClientCertificates": [
{
"SourceType":"KeyVault",
"KeyVaultUrl":"https://xxx.vault.azure.net",
"KeyVaultCertificateName":"certName"
}
]
},
You can refer this blog by damienbod to know how generate the access token in detail.
I tried to generate the access token in Postman by using parameters like below:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:clientId
client_assertion_type:urn:ietf:params:oauth:client-assertion-type:jwt-bearer
scope:https://graph.microsoft.com/.default
grant_type:client_credentials
client_assertion:client_assertion
References:
Azure AD OAuth client credential flow with certificate by Nicola Delfino
App that calls MSGraph with a certificate by christosmatskas

Installing an SSL cert for NodeJs express (for API) on my Windows server

all my servers are hosted locally. I use IIS on server 2019 to host and manage my website. I use NodeJs to act as an API which makes queries to my SQL server to get the information and return it to my React app.
my react app calls out to my NodeJs API using fetch url
var url = 'https://example.com:8080/api/locationInfo/client?client=%';
fetch(url, {agent})
.then(response => response.json())
.then(data => setLocationInfoData(data));
since my website is using HTTPS, i must use that in the URL that reaches out to the API service from NodeJs.
right now when i attempt to make these API calls, console is giving me the error: net::ERR_SSL_PROTOCOL_ERROR
im assuming this is because my NodeJs app does not have an SSL certificate installed.
I've spent many (many) hours of research, but im not experienced with the backend of web development and im having trouble understanding where and how i apply an SSL cert to my NodeJs API. I used IIS to create a self-signed certificate, but it hasnt been applied anywhere and nothing else was done; im not sure where to go from here.
if it matters, my NodeJS API is hosted on the C:\Program Files[...] directory of the server, not under the actual web hosted directories.
If i place the NodeJs app within the actual web hosted directories, would i still need a certificate? If so, how do i export the .key and the .crt files to include in my NodeJs code?
please let me know if i can provide any more information or any of my code. My experience is limited, but im determined to learn and just need some pointers to send me in the right direction.
EDIT: i created a PFX cert from windows and used openSSH to convert the key, ca, and cert to .pem and included those in my nodeJs application. now im getting "ERR_SSL_KEY_USAGE_INCOMPATIBLE"
nodeJS code:
const sslServer = https.createServer(
{
key: fs.readFileSync(path.join(__dirname, 'cert', 'key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'cert', 'cert.pem')),
ca: fs.readFileSync(path.join(__dirname, 'cert', 'ca.pem')),
},
app
)

Azure App Service Fails to Find Newly Created Certificate

I have an Azure app service I did not create but now maintain. The app service finds a certificate in a Key Vault by thumbprint and in turn uses that to get a token for doing some SQL work via nightly jobs.
It appears the certificate was set to auto renew after 80% of its valid date (12 months). The day the cert renewed my nightly jobs started to fail. I'm reasonably certain the new certificate is at the root of the problem.
As best I can tell it designed to work like this:
Job fires via Azure Logic App
annomyous POST to a reports processing API (end result should be .PDF report creation for email atachment)
API has Appsetting.json that contains the current certificates thumbprint
Thumbprint is used in the line of code below to find the certificate in the cert store
Cert is used to aquire access token and perform work
When I install both the old certificate and the new certificate on my local machine and run the entire process it works find with the old certificate and fails on this line with the new auto-generated certificate. It also fails with any new certificates I try to make in Azure and export from Azure and import to my dev machine. I've double/triple checked the appsettings to make sure the Thumbprint in question is correct and updated.
var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
When this was configured a year ago the process was to go into App Service, TLS/SSL setting blade, select Private Key Certificates (.pfx) and finally + Import Key Vault Certificate.
From there you selected the Key Vault and Certificate, then changed the Appsettings.Json to have the new Thumbprint.
Why will it work with the old (soon to expire) certificate and corresponding Thumbprint entry into appsettings but fails to work with any newly created certificates and corresponding correct Thumbprint entry?
I've looked at the Configuration for the App Service in question and it has the following setting when I understand is supposed to let the app service see all certificates registered to it, right?
WEBSITE_LOAD_CERTIFICATES = *
EDIT:
After some more testing I find that any cert I export form Azure and import on my computer will successfully iterate the cert store and find any Certificate I provide a valid Thumbprint. What it won't do is use that cert to obtain a access token. The complete code is below.
The certificate that is due to expire soon will get a proper access token and run the rest of the process by getting the proper data from the DB.
The exception I get with all the other certificates suggest something about base64 encoding but I can't quite figure that out. Any ideas?
Exception for all but the original certificate:
Client assertion contains an invalid signature
Successful Access token with this code only with original certificate:
private async Task<string> GetDatabaseTokenFromCert()
{
X509Certificate2 cert;
var store = new X509Store(StoreLocation.CurrentUser);
var authContext = new AuthenticationContext(_appSettings.AzureAD.AADInstance + _appSettings.AzureAD.TenantId);
try
{
store.Open(OpenFlags.ReadOnly);
var signingCert = store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.Thumbprint == _appSettings.AzureAD.CertificateThumbprint);
if (signingCert == null)
{
throw new FileNotFoundException("Cannot locate certificate for DB access!", _appSettings.AzureAD.CertificateThumbprint);
}
cert = signingCert;
}
finally
{
store.Close();
}
var certCred = new ClientAssertionCertificate(_appSettings.AzureAD.ClientId, cert);
var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));
return result?.AccessToken;
}
Turns out you need to also add the new certificate to the app registration. As a .cer file without the private key, obviously.
So if you get the error message:
AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found., Thumbprint of key used by client: 'YourNewCert'
Go to Key Vault, export new cert as .cer file and import it into the App Service that is trying to obtain the Access token from AcquireTokenAsync
In my case the order of operation is:
Logic app fires off anonymous call as a POST to web API
Web API uses Thumbprint of Cert in question via appsetting.json
Finds cert with thumbprint that is in App Registration of the web API
AcquireTokenAsync takes Azure info and Cert and returns Access Token
This will work or fail on this line of original post
var result = await Retry(() => authContext.AcquireTokenAsync(_appSettings.SqlConfig.ResourceId, certCred));

How to use pnp-js at a node.js application to fetch data from sharepoint?

I got a node.js application and I'm trying to use the AdalFetchClient of PnPjs to fetch some data from sharepoint.
sp.setup({
sp: {
baseUrl: "https://placeholder.sharepoint.com",
fetchClientFactory: () => {
return new AdalFetchClient("tenantId", "azure_clientId", "azure_clientSecret");
},
},
});
await sp.web.getAppCatalog().get();
I get this error: Error making HttpClient request in queryable [401] Unauthorized ::> {"error_description":"Invalid issuer or signature."}
I setup the permissions of my azure active directory app like so:
Azure App permissions
I granted all the permissions to the tenant I'm trying to fetch data from:
Granted permissions to Azure App
The example I used is here: https://pnp.github.io/pnpjs/nodejs/adal-fetch-client/
I also tried to use the AdalFetchClient with graph.. which is working. Only the sharepoint api seems to have a problem.
I found the solution. There is a AdalCertificateFetchClient which requires the following paramters:
Tenant-ID
Azure App Client ID
Thumbprint of your x.509 certificate
The private key of your x.509 certificate
The root url of the sharepoint you want to connect to
So first of all you have to create a x.509 certificate. I used this tutorial for this. (Thanks for that)
After that you have to get your thumbprint by installing the certificate to your local machine and following this steps
Last step is to get your private key of your certificate. For that you have to install openssl for windows and follow this steps
Now you can use your AdalCertificateFetchClient

how to connect to azure (management) rest api via C# in IIS

I am trying to setup a website (local testing atm), to connect to azure rest api to see our settings. I created a cert locally (W7 machine):
makecert -sky exchange -r -n "CN=azureConnectionNew" -pe -a sha1 -len 2048 -ss My "azureConnectionNew.cer"
I can see the cert in the certs MMC snap in. (do not have a right click edit permissions option when I view the cert in here).
I have a class library that setups up the connection, the cert is passed in by getting the cert (via the thumb string), this works great for the console app, but when I try and do this in a web app it all goes wrong. I get 403 errors.
I first thought that this was due to the fact that the website is running as the ApplicationPoolIdentity so doesn't have access to the cert. So I tried passing in the cert (to the same code as the console app), by loading the actual file:
var path = #"C:\temp\azureconnection\azureConnectionNew.cer";
var cert = new X509Certificate2();
cert.Import(path);
I still get 403 errors.
I tried exporting the cer file from MMC certificates snap in as a pfx file, (with private keys included). I set the local IIS set to use this cert and navigated to the https version of my local site but still got 403.
I am not sure how to include / setup / reference the cert so that IIS can send a HttpWebRequest from the server side to Azure and get a valid response.
It is always better to use Thumbprint of the certificate to get the certificate. Please make sure you have created the certificate correctly. Also please check you have placed the certificate in Personal certificate section in Local Machine. You can check this using MMC snap in. please try below code..
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var certificate = store.Certificates
.Cast<X509Certificate2>()
.SingleOrDefault(c => string.Equals(c.Thumbprint, “CertificateThumbprint”, StringComparison.OrdinalIgnoreCase)); // please replace CertificateThumbprint with original Thumbprint
This isn't the right way to use the certificate - it needs to be stored in the personal/certificates store of the user running the code (you should update the App Pool identity to be a user who can login and into whose certificates you import the cert. Here's sample code showing you how to use the service API: http://code.msdn.microsoft.com/windowsazure/CSAzureManagementAPI-609fc31a/

Resources