Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSAL verify token server-side

We're moving an app from "Sign-In with Google" to "Sign-In with Microsoft". It is an SPA, but queries an API for data. The client-side is all working using MSAL v2 (msal-browser.min.js), and we can sign in and out just fine.

When we send requests to the server, we send the JWT ID token. The server is a NodeJS API.

I can't see any Microsoft server-side Node library that has a 'verify' method we can use to validate the ID token from the client.

We've been looking at @azure/msal-node and @azure/msal-common, but can't see anything that we can feed the ID token to, to verify that the token is valid, and that the user is logged in.

We want to return 'unauthorised' from the API if the user is not logged in.

With Google, this was easy, we used google-auth-library like this:

const client = new OAuth2Client(googleClientId)
const ticket = await client.verifyIdToken({ idToken: googleIdToken, audience: googleClientId })
const payload = ticket.getPayload() // jwt payload

I hope the Microsoft equivalent is just hard to find, or it's not and I'm just being silly in not finding it.

Is there a Node library that provides a way to verify an MSAL ID token, which confirms the token is valid and that the user is signed in...?

like image 680
Stephen Last Avatar asked Dec 08 '25 01:12

Stephen Last


2 Answers

MSAL Node is for acquiring tokens so clients can access protected resources, not for validating tokens in your API. I don't think Microsoft currently provides any Node libraries for validating tokens, but you can use jsonwebtoken instead.

There is a code sample in the MSAL Node library that shows how to validate certain claims in tokens.

If you are curious what the steps to validate an Azure AD JWT are in more detail, I wrote an article that walks through the various steps. The code sample is written in Java, but all of the steps are applicable to Node as well.

like image 115
sgonzalez Avatar answered Dec 10 '25 15:12

sgonzalez


Update to show how we now validate tokens after moving from azure-ad_jwt_v2 using jwks-rsa and jsonwebtoken.

const client = jwksClient({
  jwksUri: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}/discovery/v2.0/keys`,
});

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

export async function verifyToken(token: string, callback: (err, token?: AccessToken) => void) {
  jwt.verify(token, getKey, { algorithms: ["RS256"] }, (err, decoded) => {
    if (err) {
      return callback(err, null);
    }
    const token = decoded as AccessToken;
    // Verify claims
    function confirmScope() {
      return token.scp === process.env.AZURE_FRONT_END_SCOPE;
    }

    function confirmTenant() {
      return token.tid === process.env.AZURE_TENANT_ID;
    }

    function confirmAudience() {
      return token.azp === process.env.AZURE_FRONT_END_APP_ID;
    }

    function confirmExpiration() {
      // the EXP property on a token is defined in seconds, whereas Date.now() returns milliseconds
      return token.exp * 1000 > Date.now();
    }

    const tokenIsValid =
      confirmAudience() &&
      confirmScope() &&
      confirmTenant() &&
      confirmExpiration();

    if (!tokenIsValid) {
      return callback(new Error("Invalid token"));
    }

    callback(null, token);
  });
}

Essentially the public keys for your azure tenant are accessible by anyone. The access tokens can be decoded by anyone using the public keys.

However - nobody can fraudulently create a token that would then be successfully be decoded by this public key because they lack the private signing key that lives on the azure servers.

So - get public keys, decode access token, confirm specific tenant information, good to go!

Sorry for the late response.

like image 41
northman Avatar answered Dec 10 '25 14:12

northman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!