Skip to content

Latest commit

 

History

History
122 lines (94 loc) · 5.45 KB

File metadata and controls

122 lines (94 loc) · 5.45 KB
title Performance in MSAL Node
description Learn how to measure performance in MSAL Node.
author Dickson-Mwendia
manager Dougeby
ms.author dmwendia
ms.date 05/21/2025
ms.service msal
ms.subservice msal-node
ms.topic best-practice
ms.reviewer kengaderdus

Performance in MSAL Node

Prerequisites

Measuring performance

Applications that want to measure the performance of authentication flows in MSAL Node can do so manually, using Node's performance measurement APIs or similar. Below is a list of some notable data points you can collect:

Data Meaning Suggestions
authType acquireToken* API used for the token request Use for identifying usage.
correlationId Correlation ID used for the token request. You can obtain this via the correlationId property in AuthenticationResult Use for identifying usage.
durationTotalInMs Total time spent in MSAL, including network calls and cache access Alarm on overall high latency (> 1 second). Value depends on token source. From cache: one cache access. From network: two cache accesses plus two HTTP calls.
durationInCacheInMs Time spent loading or saving the token cache persistence (e.g. Redis). Alarm on spikes.
durationInHttpInMs Time spent making HTTP calls to IdP (e.g. Microsoft Entra ID). You can use a custom network client for more fine grained monitoring. See Configuration and custom network sample for more Alarm on spikes.
tokenSource Source of the token (i.e. cache vs network). You can obtain this via the fromCache property in AuthenticationResult Tokens are retrieved from cache much faster (e.g. ~100 ms versus ~700 ms). Can be used to monitor and alarm cache hit ratio. Use together with durationTotalInMs.

For example:

    const { PerformanceObserver, performance } = require('node:perf_hooks');

    const perfObserver = new PerformanceObserver((items) => {
        items.getEntries().forEach((entry) => {
            console.log(entry);
        })
    });

    perfObserver.observe({ entryTypes: ["measure"], buffered: true });

    // ...

    performance.mark("acquireTokenByClientCredential-start");

    const tokenResponse = await msalInstance.acquireTokenByClientCredential({
        scopes: ["User.Read.All"],
    });

    performance.mark("acquireTokenByClientCredential-end");

    performance.measure("acquireTokenByClientCredential", {
        start: "acquireTokenByClientCredential-start"
        end: "acquireTokenByClientCredential-end"
        detail: {
            tokenSource: tokenResponse.fromCache
            correlationId: tokenResponse.correlationId
        }
    });

Performance considerations for confidential client applications

Since confidential client applications are primarily used with server-side scenarios involving concurrent processing of requests, we recommend to scope MSAL ConfidenticalClientApplication (CCA) instances to each user, request or session.

A fresh CCA instance for each request means its default in-memory cache will initially not contain any tokens or metadata about how to acquire tokens. As such, the CCA instance you create should be prepared before the token request in order to avoid poor performance.

import {
    ConfidentialClientApplication,
    AuthenticationResult,
    CryptoProvider,
    OnBehalfOfRequest
} from "@azure/msal-node";

function getMsalInstance(partitionKey: string): ConfidentialClientApplication {
    return new ConfidentialClientApplication({
        auth: {
            clientId: "ENTER_CLIENT_ID",
            authority: "http://login.microsoftonline.com/ENTER_TENANT_ID"
            cloudDiscoveryMetadata: "PROVIDE_STRINGIFIED_DISCOVERY_METADATA"
            authorityMetadata: "PROVIDE_STRINGIFIED_AUTHORITY_METADATA"
        },
        cache: {
            cachePlugin: new CustomCachePlugin(
                this.cacheClientWrapper,
                partitionKey
            )
        }
    });
};

async function getToken(tokenRequest: OnBehalfOfRequest): Promise<AuthenticationResult | null> {
    const partitionKey = await this.cryptoProvider.hashString(tokenRequest.oboAssertion);

    const cca = getMsalInstance(partitionKey);

    let tokenResponse = null;

    try {
        await cca.getTokenCache().getAllAccounts(); // required for cache read
        tokenResponse = await cca.acquireTokenOnBehalfOf(tokenRequest);
    } catch (error) {
        throw error;
    }

    return tokenResponse;
};

See (CCA) Web API using a custom distributed cache plugin for more.

See also