Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure AD B2C - Token validation does not work

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 siganture is invalid.

What am I missing?

like image 400
martinoss Avatar asked Jun 02 '17 13:06

martinoss


People also ask

How token are validated?

You can validate your tokens locally by parsing the token, verifying the token signature, and validating the claims that are stored in the token. Parse the tokens. The JSON Web Token (JWT) is a standard way of securely passing information. It consists of three main parts: Header, Payload, and Signature.

How is bearer token validated?

The used key is typically identified by the "kid" (key ID) header parameter. JWT claims check -- The JWT claims set is validated, for example to ensure the token is not expired and matches the expected issuer, audience and other claims.

How long is Azure AD token valid for?

By default, access tokens are valid for one hour, when they expire the client is redirected to Azure AD to refresh them.


2 Answers

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

like image 167
Saca Avatar answered Sep 23 '22 22:09

Saca


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

  • jwks-rsa: "1.8.1"
  • jsonwebtoken: "8.5.1"

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);
   });

like image 36
anAgent Avatar answered Sep 23 '22 22:09

anAgent