I'm sending out the signal flare after exhausting my search efforts. I feel I'm real close to getting this working but hit the wall. Below details an example of what I'm trying to accomplish and the steps taken so far. Please point out errors and make suggestions where able.

I have an OAUTH/OPENID/OIDC application that's registered in Azure and want to use a certificate to authenticate my client instead of a client secret.

I generated a certificate, exported and uploaded the public portion to the "certificates and secrets" section of the configuration. Thumbprint: 3BC87980310C490A62AA5F6343D4C55DF8EBBA85

Manifest with modified values...

"keyCredentials": [ { "customKeyIdentifier": "3BC87980310C490A62AA5F6343D4C55DF8EBBA85", "endDate": "2020-10-10T19:45:00Z", "keyId": "ff3ce8e8-7268-4b46-88be-d3a191a0695e", "startDate": "2019-10-10T19:45:00Z", "type": "AsymmetricX509Cert", "usage": "Verify", "value": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0....", "displayName": "L=mycity, E=myemail@email.com, CN=jarred-test-oauth, OU=Federation, O=12345"

The authentication is simple enough and am able to obtain an authorization code. I signed my client assertion JWT with the Private certificate:

Header: { "alg": "RS256", "typ": "JWT" }

Payload: { "iss": "2f877daa-b6f5-42a3-8430-acf238b234e1", "sub": "2f877daa-b6f5-42a3-8430-acf238b234e1", "nbf": 1570803651, "exp": 1570807251, "iat": 1570803651, "jti": "3BC87980310C490A62AA5F6343D4C55DF8EBBA85", "typ": "JWT" }

"iss" and "sub" are the client_id of my app within Azure.

I tried to test the exchange of the code for tokens with the following in Postman:

POST: https://login.microsoftonline.com/my-tenant/oauth2/v2.0/token

Headers: Content-Type = application/x-www-form-urlencoded

Body: grant_type = authorization_code

code = obtained_code

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

client_assertion = eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIyZjg3N2RhYS1iNmY1LTQyYTMtODQzMC1hY2YyMzhiMjM0ZTEiLCJzdWIiOiIyZjg3N2RhYS1iNmY1LTQyYTMtODQzMC1hY2YyMzhiMjM0ZTEiLCJuYmYiOjE1NzA4MDM2NTEsImV4cCI6MTU3MDgwNzI1MSwiaWF0IjoxNTcwODAzNjUxLCJqdGkiOiIzQkM4Nzk4MDMxMEM0OTBBNjJBQTVGNjM0M0Q0QzU1REY4RUJCQTg1IiwidHlwIjoiSldUIn0.t8lArFodXkHO9Ps9O3q7VH55pRl6NtcIkEbSz-hDL0V6I7iWi4N-1VBNM_nFUHkNhBoGaskV0eQtqMXYildb7oEr75KgbcjacZy2OI319uPwztHp9jVxjsBhB_rKXND4M6URr23IWkLwFb2008vq_fY4trLUZR9ILZOE0Dr_MdaQmrt8fU9mYNkSEnRsiXKuqcS97oBfo6-9MuDbkcNuAOxZnsmbYvutk1LeabFywbc4qO3dgb8PtfqMAiYxgYTzg72tAw-ncq6uRXgG5XoxJVOExCyn5CXV9lSsE33_oekOEfRU5CyC0IvtSLhSoZ7LKtSMJ22ZXiyFqvddenJC8w

Sending the request yields the following error from Azure (in postman):

{ "error": "invalid_request", "error_description": "AADSTS5002723: Invalid JWT token. No certificate thumbprint nor keyId specified in token header.\r\nTrace ID: 89f69560-9ae7-482f-803c-9faa71d44100\r\nCorrelation ID: e2ebab72-8b4d-47a6-85be-14893158dd5e\r\nTimestamp: 2019-10-11 14:23:32Z", "error_codes": [ 5002723 ], "timestamp": "2019-10-11 14:23:32Z", "trace_id": "89f69560-9ae7-482f-803c-9faa71d44100", "correlation_id": "e2ebab72-8b4d-47a6-85be-14893158dd5e" }

I assume The header is supposed to contain a KID or 'thumbprint' value. I don't have a KID as I'm not setting up a public facing JWKS for this purpose, and not sure what to enter as a field for thumbprint.

Very much appreciated if someone could point me in the right direction. I've already seen all the references to others that have posted questions. I could really use someone with knowledge on what to try next.

Here are some tools and documents that have gotten me this far:

JWT signing tool - http://kjur.github.io/jsjws/tool_jwt.html

Oauth client authentication explained - https://medium.com/@darutk/oauth-2-0-client-authentication-4b5f929305d4

RFC 7523 OAuth JWT Assertion Profiles - https://tools.ietf.org/html/rfc7523#section-2.2

Microsoft blog - https://blogs.msdn.microsoft.com/exchangedev/2015/01/21/building-daemon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow/

JSON Web Token (JWT) - Claims and Signing draft-jones-json-web-token-01 - https://tools.ietf.org/id/draft-jones-json-web-token-01.html

KB for Client Authentication - https://kb.authlete.com/en/s/oauth-and-openid-connect/a/client-secret-jwt

Thank you!


  • ok, made it a slight bit further in testing. Found that "x5t" could be used to include thumbprint, and also needed an 'aud' in the payload. – Jarred Oct 11 '19 at 18:25
  • ok, made it a slight bit further in testing. Found that "x5t" could be used to include thumbprint, and also needed an 'aud' in the payload. { "alg": "RS256", "typ": "JWT", "x5t":"?" } { "iss": "2f877daa-b6f5-42a3-8430-acf238b234e1", "sub": "2f877daa-b6f5-42a3-8430-acf238b234e1", "nbf": 1570813777, "exp": 1570817377, "iat": 1570813777, "jti": "?", "typ": "JWT", "aud": "tokenendpoint" } – Jarred Oct 11 '19 at 18:30
  • The fields I'm unsure about now are the 'jti' and 'x5t'. The JWT is signed with the Private key, but using the thumbprint of the key yields an error. "error_description": "AADSTS700027: Client assertion contains an invalid signature. [Reason - The key was not found., Thumbprint of key used by client: '62643165643263383337363130383338346565323733613138303161303766303862333164373234', Please visit 'developer.microsoft.com/en-us/graph/graph-explorer' and query for 'graph.microsoft.com/beta/applications/2f877daa- – Jarred Oct 11 '19 at 18:31
  • b6f5-42a3-8430-acf238b234e1' to see configured keys]\r\nTrace ID: de7154c6-21cb-4a40-855c-f26cdd483200\r\nCorrelation ID: 4c61b889-1e74-49f2-835c-d578230a976b\r\nTimestamp: 2019-10-11 18:28:39Z", "error_codes": [ 700027 ], "timestamp": "2019-10-11 18:28:39Z", "trace_id": "de7154c6-21cb-4a40-855c-f26cdd483200", "correlation_id": "4c61b889-1e74-49f2-835c-d578230a976b", "error_uri": "login.microsoftonline.com/error?code=700027" } – Jarred Oct 11 '19 at 18:31

Had to panic before finding the answer. 5 days of searching and here's my findings in case anyone else finds this question and needs help.

Quick Steps:

  1. Generate your private key (the below uses PowerShell)

  2. Generate your Public certificate

  3. Convert your public cert and private key in to a PEM format

  4. Upload your public certificate in to the application configuration under 'certificates and secrets'

  5. Obtain the Thumbprint of your certificate in Base64 (either grab while creating certificate or look up from keystore or other location)

$cert=New-SelfSignedCertificate -Subject "CN=$ApplicationName" -CertStoreLocation "Cert:\CurrentUser\My"  -NotAfter (Get-Date).AddMonths(24) -KeyExportPolicy Exportable -KeySpec Signature
$bin = $cert.RawData
$base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cert.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
  1. build your JWT (I used https://jwt.io/)


  "alg": "RS256",
  "typ": "JWT",
  "x5t": "<Base64 Thumbprint>"


  "iss": "<clientid>",
  "sub": "<clientid>",
  "exp": 1570838377 (expiration time),
  "jti": "<random unique identifier>",
  "aud": "https://<token-endpoint>"

Drop your private key (PEM) in to the bottom verify-er which will sign your JWT in the "Encoded" window.

  1. Obtain your authorization code
  2. Craft your request for tokens


Content-Type: application/x-www-form-urlencoded


code=OAQABAAIAAACQN9QBR.... (your authorization code)
client_assertion=eyJhbGciOiJSUzI1NiIsInR5.... (your signed JWT)
state=state (optional)
redirect_uri=https%3A%2F%2Fblackhole.com (The callback URI)
  1. POST to your token endpoint. If all goes as planned, you should receive your tokens.
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.