6

I would like to use Azure AD B2C but have several difficulties using it. One problem I have is to validate the signature of the token. First I wanted to validate the token "manually" using jwt.io.

According to the Microsoft Docs, validating the signature should work like this:

Your app can use the kid claim in the JWT header to select the public key in the JSON document that is used to sign a particular token. It can then perform signature validation by using the correct public key and the indicated algorithm.

My understandig: Grab the kid value out of the header, lookup the key in the metadata under the location of jwks_uri, (assumption) use the value of "n" to verify the signature.

enter image description here

But Jwt.io, jsonwebtoken.io, and jose-jwt all say, that the signature is invalid.

What am I missing?

kometen
  • 6,536
  • 6
  • 41
  • 51
martinoss
  • 5,268
  • 2
  • 45
  • 53

3 Answers3

9

Jwt.io seems to only support HS265 with a string secret and RS256 with a string secret or a certificate.

Azure AD B2C uses the more native form of RS256 which as per RFC 3447, section 3.1 defines that the public key consists of two components: n and e. The JWK contains both n and e which can be used to generate public key and validate the token signature.

In order to use Jwt.io, you'll need to convert Azure AD B2C's n + e format for the key to a cert format. See this example for a reference on how to do this: Go Language Convert Modulus exponent to X.509 certificate

Community
  • 1
  • 1
Saca
  • 10,355
  • 1
  • 34
  • 47
  • 2
    That was it! Thank you for pointing me in the right direction. I was finally able to turn the modulus and exponent to PEM format [using c#](https://stackoverflow.com/questions/28406888/c-sharp-rsa-public-key-output-not-correct/28407693#28407693) and was able to verify the signatrue using jwt.io and c#. – martinoss Jun 05 '17 at 12:30
  • would love to use jwt.io for this, [posted a thread to their forum](https://community.auth0.com/questions/7687/cant-verify-signature-of-jwt-from-azure-b2c) – spottedmahn Aug 20 '17 at 18:55
  • jwt.ms will perform validation on AAD and AAD B2C tokens as well as decode – Garrison Neely Mar 15 '22 at 22:53
6

There is now a way to validate tokens using two npm packages.

Resources

The information was made available at: Microsoft - Example-verifying-validation-tokens

Package Versions Used

How To

The example below was modified from example in Microsoft - Example-verifying-validation-tokens

Retrieving "Issuer" and "User Flow" Name Values From Azure

Screenshot of Azure portal for

Retrieving Application Id

enter image description here

Code Example

import jwt from 'jsonwebtoken';
import jkwsClient from 'jwks-rsa';

// Variables that need to be defined based on your Azure B2C Configuration.
const jwksUri = 'https://MY-B2C-TENANT.b2clogin.com/MY-B2C-TENANT.onmicrosoft.com/MY-USER-FLOW-NAME/discovery/v2.0/keys';

const client = jkwsClient({
  jwksUri
});

export function getKey(header, callback) {
  client.getSigningKey(header.kid, (err, key) => {
    var signingKey = key.getPublicKey();
    callback(null, signingKey);
  });
}

export function isTokenValid(token, applicationId, issuerUri) {
  return new Promise((resolve) => {
    const options = {
      audience: [applicationId],
      issuer: [issuerUri]
    };
    jwt.verify(token, getKey, options, (err, decoded) => {
      if (err) {
        // eslint-disable-next-line no-console
        console.error('Jwt Validation Failed', err);
        resolve(false);
      } else {
        // eslint-disable-next-line no-console        
        console.debug(decoded)
        resolve(true);
      }
    });
  });
}

Usage

const applicationId = 'APPLICATION ID OF B2C TENANT'
const issuerUri = 'In the User Flow Properties'

const valid = isTokenValid('some token value without "Bearer", applicationId, issuerUri);

Notes

  • The jwksUri contains the user "User Policy". If you have multiple "User Policies", you will need to figure out how to instantiate the jkwsClient with correct jwksUri based on the each "User Policy" flow.
  • While you can leverage jwt.io or jwt.ms, a token can be decoded in the debugger console. This will give you some key information such as the "iss" (Issuer) and the "kid":
'e3R5cDogIkpXVCIsIGFsZzogIlJTMjU2Iiwga2lkOiAiWU9VX1NORUFLWV9SQUJCSVQifQ==.e2V4cDogMTU5NjYwMTc4NiwgbmJmOiAxNTk2NTk4MTg2LCB2ZXI6ICIxLjAiLCBpc3M6ICJodHRwczovL3doaXRlLXJhYmJpdC5iMmNsb2dpbi5jb20vZjIzNDZhMzBhLTk1ODEtNGU0ZC04MWQwLWQyZjk4NTQ3MWJhOS92Mi4wLyIsIHN1YjogImYzMmNjNmJhLWE5MTctNGE1Ni1hYjhmLWIyNGZmMTg1ODUyOCIsICIuLi4iOiAibW9yZSB2YWx1ZXMuLi4ifQ==.UNODECODEME'.split('.').map((value, index) => { 
   // The signature can't be decoded
   if(index > 1) { return value; }
     return atob(value);
   });

anAgent
  • 2,550
  • 24
  • 34
  • You deserve a medal this is the best documentation I have found about this topic. – Remo H. Jansen Dec 18 '20 at 13:25
  • Something that might help other... here is an example of a valid issuer URI `issuer = 'https://ugrose.b2clogin.com/9c2984ff-d596-4e5c-8e74-672be7b592e3/v2.0/' # iss` – Remo H. Jansen Dec 18 '20 at 13:47
  • This article is a good complement to this answer https://robertoprevato.github.io/Validating-JWT-Bearer-tokens-from-Azure-AD-in-Python/ – Remo H. Jansen Dec 18 '20 at 13:48
0

You can use https://play.golang.org/p/7wWMBOp10R to get a public key from modulus(n) and exponent(e). Then copy the entire output in jwt.io and you'll see the signature is valid.