Introduction

In an asymmetric algorithm, a JWT token is signed with an Identity Provider’s private key. To verify the signature of the token, one will need to have a matching public key. This post will cover how to use the JWT tool at https://jwt.io/ to verify the signature of an signed Azure AD token (either access or id token).

Note:

  • You should only validate the token intended for your own resource.  Using the technique below to validate the signature of a Microsoft First Party Apps token (for example token audience is for Microsoft Graph resource) may fail.
  • http://jwt.io utility is a 3rd party tool.  We (Microsoft) have no knowledge of how this site utilizes the token information.
  • To see the token claim information, we recommend using the Microsoft utility http://jwt.ms since the site does not cache token information.

Verifying the token signature

  1. Browse to https://jwt.io/ and paste the JWT token into Encoded text box. The tool should automatically detect the token’s signature algorithm (RS256) and displays the token into 3 parts: header, payload, and signature. Note the “kid” field in the header. This is the key id of the certificate used to sign the token

    Scrolling down a little you will see the version of the token (v1 token in this case) and it will say “invalid signature”. This is expected since at this point we have not provided any certificate info for the tool to verify the token signature.

  2. Find the jwks URL info from Azure AD’s OIDC well-known endpoint. Depending upon your token version, use the correct well known endpoint (make sure to supply the correct tenant name in the well known URL):

    V1 token: https://login.microsoftonline.com/{tenant name}/.well-known/openid-configuration

    V2 token: https://login.microsoftonline.com/{tenant name}/v2.0/.well-known/openid-configuration

    For my case, I use the V1 OIDC endpoint. You can either paste the URL into a web browser or postman to find the “jwks_uri” field from the response:

  3. From the JWKS URI endpoint, find the key that has a matching kid (key id) as the token. Copy the long text string from the key’s x5c field. This is the public key section

  4. Enclose the x5c string in the BEGIN CERTIFICATE / END CERTIFICATE block as followed (see example at the end post)

    ‑‑‑‑‑BEGIN CERTIFICATE‑‑‑‑‑

    MIIDBTCCAe2gAwIBAgIQKOfEJNDyDplBSXKYcM6UcjANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MTIyMjAwMDAwMFoXDTIwMTIyMjAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8ZT56lunzbgm3a4QxM8BiVbsd4j77bf/K/rhxyCmuwv2/7seYXsDTEvdUoFD2Tq7Km+eLh4/+yDViqihLRXOGD/NxYbLP7jv5k/e3MxDbOM1mkjfRMMPxc9+sav7meue+dJRysF0CdQP6XvlToDZT4PBAou5nkAydOa/N/HrtY6ShY8ZEK3URUy4GLUUO08V/s80cqEUfIqiXOkb54o4dffmH1rQbAiNa9du0hWpFAa2P2SrCshPSjVlC+x+uRMhUTYCvNF32L4UJRsN/gI39vH4u9cFvgcqStW0wgK88F+84Bdx+j9bvDyqLEjkjf0PfkHPV/kf2Pt2zqTiIizr8CAwEAAaMhMB8wHQYDVR0OBBYEFC//HOy7pEIKtnpMj4bEMA3oJ39uMA0GCSqGSIb3DQEBCwUAA4IBAQAIYxZXIpwUX8HjSKWUMiyQEn0gRizAyqQhC5wdWOFCBIZPJs8efOkGTsBg/hA+X1fvN6htcBbJRfFfDlP/LkLIVNv2zX4clGM20YhY8FQQh9FWs5qchlnP4lSk7UmScxgT3a6FG3OcLToukNoK722Om2yQ1ayWtn9K82hvZl5L3P8zYaG1gbHPGW5VlNXds60jIpcSWLdU2hacYmwz4pPQyvNOW68aK/Y/tWrJ3DKrf1feDbmm7O5kpWVYWRpah+i6ePjELNkc2Jr+2DchBQTIh9Fxe8sz+9iOyLh9tubMJ+7RTs/ksK0sQ1NVScGFxK+o5hFOOMK7y/F5r467jHez

    ‑‑‑‑‑END CERTIFICATE‑‑‑‑‑

  5. Now copy entire text above into the 1st textbox under “Verify Signature” section and the Invalid Signature text should change to “Signature Verified”

 

Conclusion:

The above steps show a manual way to validate the JWT token’s signature given the certificate’s public key.

References:

https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-openid-connect-code

https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc

for clarity:

Example public certificate section

5 dashes followed (no space) by BEGIN CERTIFICATE followed by 5 dashes. The end section is in the same format

-----BEGIN CERTIFICATE-----
MIIC8DCCAdigAwIBAgIQUdK9w17K+rNOSRJZP83bIjANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylNaWNyb3NvZnQgQXp1cmUgRmVkZXJhdGVkIFNTTyBDZXJ0aWZpY2F0ZTAeFw0xOTA0MTgwMDU5MzNaFw0yMjA0MTgwMDU5MzNaMDQxMjAwBgNVBAMTKU1pY3Jvc29mdCBBenVyZSBGZWRlcmF0ZWQgU1NPIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArkGIwWMl5Z6bQDsRZocoylG2Nf91JlBWbGKULJCu8yzoFT5C4JgJ6qoYXjKNIq/+DI8aWsxMclYHmsTRl8JMWaCH1K4BaApYGPYLBjL8DKXm+ELSp9VyAPzD+FYElGCpU+B3FvWcEQvGFBzajhn17J5zutN6I8rGngIK0ewqpekGV85CEFQ5EFwgx7lkF5PLvgiTBq08b8xNB3f0laSGMH7MYjDmGh//Zb2QT/6S7ZzB85YnGtlOxaLjKeYcM8hBdVK5lYqvQb7a0GWmUxmlwClwN3XpBBdg274hBG4ynkWLnx/0Vb5/RfWa0HCRa4JK28sfI/VufdR0SJ+WBZWXZwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA6IpznhiMSBayXGZaw0o8mPtI9qkXaFF5kDLqZFYN1tVTAukG3Q//PPnwsGLdRHFGOn3OwPNwo7XtmM6iyIunKAjTqtf7zA53umWirJzq85l6NVpqtQf7CABOzY3I6NKEa7wmYPBD6Egh6PqJO9O1ppAqJT4z/0lYTIEac7KYMdv/FwpYd0w4Y4gbYtRODO9iQwAXzzM243oM8XSrrqQj3smfNyrZfZS8XWvjQGJy9MTn2Ybvy9IUhVwEWJTyeTKBhnBk9ujNCcEG4kAYL1uWGun+dT2h4vbyNKxMlomryz3actMp90QEefZTZ/kOxqAthenbou+WF0OfhCH9Zhd04
-----END CERTIFICATE-----

15 Thoughts to “Using jwt.io to verify the signature of a JWT token”

  1. Mark

    if I don’t find the kid in https://login.microsoftonline.com/<tenantId>/discovery/v2.0/keys, what can be the reason? I’m using AAD b2c, in case this makes a difference.

  2. Mark

    aha, for B2C, one has to use
    https://<tenantName&gt;.b2clogin.com/<tenantName>.onmicrosoft.com/<loginPolicyNameUsedToAcquireToken>/v2.0/.well-known/openid-configuration

    to get the config and then from there to the keys discovery

    1. Bac Hoang [MSFT]

      Hi Mark,
      Yes for Azure AD B2C, refer to https://docs.microsoft.com/en-us/azure/active-directory-b2c/openid-connect for documentation on metadata endpoint.

  3. chris ondrovic

    any idea how to get it validate tokens with the audience of graph.microsoft.com? Seems like it always returns invlaid signature

    1. Bac Hoang [MSFT]

      This is expected as the blog mentioned. You should not try to validate the MS Graph Access Token. You should only validate access token meant for your own application. Nobody can validate MS Graph Access Token besides MS Graph resource since that token is encoded in a special format.

  4. Rajesh

    Hello, Do we have a java API to validate the token signature. in Our scenario , we have a mobil app calling java web app. The mobil app will pass the token to java web app , and we want to validate the token signature.

    Also, do we have an example of a mobil app calling a Java web app and passing thr toke. I want to understand how to register both apps and the flow to use ( Like we have auth code flow – want to know which flow can be used in this scenario)

  5. Daneil

    I have an access_token with issuer sts.windows.net, and ver: 1.0, but I still cannot validate it with the corresponding x5c from its kid. I also realized that both of the jwks_uri from the Azure’s OIDC well-known endpoints give us the exact same public keys, with the only difference being that the version 2 has the issuer property; the rest stays the same. That being said, how to validate v1 token? Is there something I missed?

    1. Bac Hoang [MSFT]

      As long as your access token is for your own API (not any MS 1st party app) and you have a matching public key (from the jwks_uri endpoint) for the token signing certificate then the steps in this article should work.

      1. Daneil

        I do get a token after signing myself in (implicit flow), but how do I make sure that the access_token is for my own API?

        1. Bac Hoang [MSFT]

          You can use the https://jwt.ms tool to inspect the aud claim (https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#payload-claims) in the token to see if the token is for your web API or not.

  6. Mark B.

    I’m adding your X5C key surrounded with the BEGIN/END CERTIFICATE lines into the https://www.sslshopper.com/certificate-decoder.html and see that this certificate is invalid.
    I also try to do the same with other certificates from https://login.microsoftonline.com/common/discovery/keys – all of them appear invalid.
    If I try to validate signature by “jsonwebtoken” library:

    jwt.verify(access_token, pubcert, function(err, decoded) {
        console.log(decoded.foo) // bar
    });
    

    with Microsoft Graph API access_token and pubcert created like explained in this article I receive an error:
    ERR_OSSL_PEM_NO_START_LINE
    error:0909006C:PEM routines:get_name:no start line

    1. Bac Hoang [MSFT]

      As the article mentioned, you can not and should not validate Microsoft Graph Access token since that token is encoded in a special format. Try to validate an Access Token for your own web API.

      1. XD

        So tf are u validating? what token are u validating bro? no sense

        1. Bac Hoang [MSFT]

          you validate the access token for your own web API registered in Microsoft Entra ID

Leave a Reply to Daneil Cancel reply