In this post, my aim is to demonstrate, in a deliberate semi-manual fashion, how to obtain the access token via Azure Service Principal with Certificate-based Authentication from Linux (e.g. CentOS 6.6) command line using a few tools and raw REST calls.

Let's get started.

Generate Certificate

Use openssl command to generate a self-signed certificate and protect it with a PEM pass phrase (e.g. P@ssword123):

Certificate Thumbprint

To be able to uniquely identify the certificate, let's get its hexadecimal thumbprint:

openssl x509 -in cert.pem -fingerprint -noout

We will actually need the thumbprint converted from its hexadecimal representation to base64. We can use sed to replace the colons and remove "SHA1 Fingerprint=" substring, xxd to convert to bytes, and base64 to encode.

Once Azure CLI is installed and you have logged in using your "organizational account", switch to the "ARM mode":

azure config mode arm

Create Application and its Service Principal

Use Azure CLI to create an "Application" in the Azure Active Directory of your subscription by passing the public key value from cert.pem file (as you recall from above tail/head are there to skip the first and last lines):

NOTE: If you get an error message saying "'ad' is not an azure command. See 'azure help'", double check that you switched to the ARM mode via "azure config mode arm".

Create the "Service Principal" object for the Application Id that was created in the previous call.

azure ad sp create <Copy and Paste Application Id GUID Here>

Assign proper role to the "Service Principal" using its Object Id. In this example, we will assign the "Contributor" role to the service principal at the scope of the subscription so that it can access and make changes to any resource group within the subscription.

Install node.js package jsonwebtoken

Visit http://jwt.io/ to learn more about the JSON Web Token format and see many other libraries for JSON Web Token signing/verification available in various programming languages. I have picked Node.js since it is already available on the Linux VM after installing the "Azure CLI". In addition, we need to make sure that the library we use supports RSA-SHA256 algorithm that is expected by Azure Active Directory when using certificate authentication.

Obtain Azure Active Directory Tenant Id

We also need to obtain our Azure Active Directory Tenant Id GUID to specify it within the token payload. Since we already have Azure CLI installed, we can obtain the Tenant Id by running the following command and copy-and-pasting the value returned in the "Tenant Id" column.

azure account list

azure account show "Arsen Subscription Name"

Sign the JWT Token

Create a small node.js script that we will call signjwt.js to sign the client assertion specifying the base64 encoded certificate thumbprint in the "x5t" additional header. You will recall that we obtained this base64 value in one of the earliest steps.

The other standard "claims" of the JSON Web Token should contain the following values:

"aud" (Audience) is set to https://login.microsoftonline.com/TENANT_ID _HERE/oauth2/token

"iss" (Issuer) and "sub" (Subject) are all set to the Application Id GUID

"jti" (JWT ID) is set to a unique identifier for the JWT (in this example I will set it to a random value)

"nbf" (Not Before) is set to the UNIX timestamp denoting the start of the token validity period

"exp" (Expiration Time) is set to the UNIX timestamp denoting the end of the token validity period

Validate Signed JWT Token

We can use http://jwt.io/ to validate that the token is parsing correctly. The signed token consists of three parts: base64(header).base64(body).base64(signature) without the "==" at the end.

Example of what JWT looks like with a signature:

Get Access Token from Azure Active Directory

We will use Postman, Fiddler, or any of your other favorite HTTP request builder tools to manually submit a raw HTTP request to the Azure Active Directory endpoint passing in all of the required parameters and obtaining back the Bearer access token that we can use to access Azure Resource Manager APIs.

This should be set to a specific value telling Azure Active Directory OAuth endpoint that we will be using certificate authentication instead of "password"

urn:ietf:params:oauth:client-assertion-type:jwt-bearer

client_assertion

This is the signed JWT token. It will be a long value. Remember, it was signed using our little signjwt.js script and the private key from the key.pem file. Azure Active Directory will validate this assertion's signature using the public key (cert.pem) that we submitted to Azure when creating the application via Azure CLI.

resource

This is the endpoint for the resource that we are asking Azure Active Directory to issue us a token for. In case of Azure Resource Manager, it should be set to the following URL since that REST API start with this value https://management.azure.com/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/{provider-namespace}/

Conclusion

By now we should have a pretty good understanding of how to manually authenticate via Azure Active Directory using an application Service Principal and certificates, how to obtain the OAuth access token, and how to use it to invoke Azure Resource Manager REST APIs – all from Linux command line.

Whenever possible, I recommend using the available Azure SDKs with all of the inbuilt helper methods. However, I also find it useful to manually step through a process to get a deeper understanding of what is happening behind the scenes.

Thanks for pointing this out. Yes, all parameters should use a single dash “-“, but I think a few got changed to a longer-dash inadvertently when I copied-and-pasted. I fixed the -noout, but there could be other occurrences of this issue. Therefore, if you copy-and-paste commands from the article and they don’t work for you, please double check the dashes.

The “Obtain Azure Active Directory Tenant Id” section should be updated a little. My azure cli version (0.10.8) doesn’t list the tenant id with the “azure list account” command. I am using the following which does the trick: azure account show –json | jq -r ‘.[0].tenantId’

It looks like a the description of the values passed in the “Sign the JWT Token” section could use some updating. “jti (JWT ID)” states that the Application GUID is being used in the example but the example shows Math.random(). “jti” is immediately listed on the next line with the “nbf” description causing slight confusion.