Entra ID: Obtaining a JWT Access Token Using an SSL Certificate

Oguzhan YIGIT
Published by Oguzhan YIGIT
Category : Azure / Azure Active Directory / OAuth
30/01/2024

On this post, I share a way to get a JWT token on Entra using a client SSL certificate instead of a secret stored at the Entra level.

Entra (formerly Azure AD), offers a full range of features for setting up Oauth 2.0 authentication scenarios. You can consult my various publications on this subject:

 

OAuth 2.0 and SSL authentication

Of course, it’s possible to combine client/secret JWT tokenization with mutual SSL authentication to the target API (in other words, the backend service). However, it is also possible to dispense with secrecy to obtain the access token, by using the certificate to authenticate to Entra.

 

Configuring an OAuth2 flow with a certificate

You need to declare an App Registration client on Entra. You need to associate your SSL certificate with it on the “Certificates and secrets” tab. The easiest way to do this is via Entra screens in Azure portal. Obviously, only the public key needs to be shared with Entra.

upload public key

 

Make sure the certificate is intended for electronic signatures. Once the certificate is associated with the account, it is possible to request a JWT token.

 

Obtaining an access token using the certificate

I’ve configured the OAuth 2.0 Client_Credentials flow and added a few authorizations to the Graph API for this client. To obtain the access token that lists my authorizations on the API Graph, it is necessary to consume the endpoint /token with the following body:

grant_type=client_credentials&client_id=<clientId>&scope=<scope>&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=<client_assertion>

The query parameters are :

  • Content-Type (header): application/x-www-form-urlencoded
  • grant_type (body): client_credentials
  • client_id : client identifier (App Registration)
  • scope : targeted scope, in my case “https://graph.microsoft.com/.default” for GraphAPI
  • client_assertion_type : urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • client_assertion : JWT assertion (description below)

 

JWT assertion generation

It’s not necessary (or even possible) to present your client certificate to obtain your token! Instead, you need to generate a JWT assertion (client assertion), and sign it with your certificate. This assertion consists of :

  • a header
  • a body
  • a signature

And looks like this:

{
  "alg": "RS256",
  "typ": "JWT",
  "x5t": "hOBcHZi846VCHSJbFAs26Go9VTQ"
}
.
{
  "aud": "https: //login.microsoftonline.com/contoso.onmicrosoft.com/oauth2/v2.0/token",
  "exp": 1484593341,
  "iss": "<clientId>",
  "jti": "22b3bb26-e046-42df-9c96-65dbd72c1c81",
  "nbf": 1484592741,
  "sub": "97e0a5b7-d745-40b6-94fe-5f77d35c6e05"
}
.
"Gh95kHCOEGq5{a lot of characters here}KKJDg"

 

Note: The dot separating each part is required.

 

Header composition

The header consists of the following fields:

  • alg: hardcoded, RS256
  • typ: hard-coded, JWT
  • x5t: certificate thumprint hash in base 64

 

Header composition

  • aud: audience, must be https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
  • exp: expiration time, token expiration date/time. Must be greater than current date/time and has nbf
  • iss: issuer of the token, in this case the client id
  • jti: a guid, unique identifier of the token we generate
  • nbf: not before, date from which the token is valid
  • sub: subject claim, in this case, we must also use the App Registration client id
  • iat: issued at, optional and used to determine the token generation date

 

Signing and generating the JWT assertion

Once you’ve generated your header and body, you need to sign it with your certificate. Of course, you need to use your certificate’s private key to perform this operation. The resulting signature will guarantee that your assertion conforms to what you generated and has not been altered during transfer to your target API.

Finally, you need to convert your message into a JWT assertion.

 

Test obtaining the JWT token

To test the call to Entra, I use Postman. Calling the endpoint /token with the items listed above allows me to obtain an access token with a certificate instead of a client secret.

test postman jwt certificate entra id

 

Using libraries

I recommend using one or more libraries that automatically generate assertions from your elements, such as MSAL.Net.