MSAL ノードでのトークン キャッシュ

MSAL ノードは、トークンを取得すると、後で使用するためにメモリにキャッシュします。 MSAL ノードは、トークンの有効期間と更新を管理します。 acquireTokenSilent()などの API は、特定のアカウントのキャッシュからアクセス トークンを取得します。

MSAL では、セキュリティ上の理由から更新トークンは公開されません。 更新トークンを取得する方法については、 FAQ を参照してください。

クライアント シークレットを安全に使用する

クライアント シークレットをハードコーディングしないでください。 dotenv npm パッケージを使用すると、シークレットを .gitignore に含める必要がある .env ファイル (プロジェクトのルート ディレクトリにあります) にシークレットを格納し、シークレットが誤ってアップロードされるのを防ぐことができます。

const msal = require('@azure/msal-node');
require('dotenv').config(); // process.env now has the values defined in a .env file

// Create msal application object
const cca = new msal.ConfidentialClientApplication({
    auth: {
        clientId: "Enter_the_Application_Id_Here", // e.g. "00001111-aaaa-2222-bbbb-3333cccc4444" (guid)
        authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here", // e.g. "common" or your tenantId (guid)
        clientSecret: process.env.clientSecret // obtained during app registration
    }
});

/**
* acquireToken* APIs return an account object containing the "homeAccountId"
* you should keep a record of this in your app and use it later on when calling acquireTokenSilent
*/
const someUserHomeAccountId = "Enter_User_Home_Account_Id";

const msalTokenCache = cca.getTokenCache();
const account = await msalTokenCache.getAccountByHomeId(someUserHomeAccountId);

const silentTokenRequest = {
    account: account,
    scopes: ["User.Read"],
};

cca.acquireTokenSilent(silentTokenRequest).then((response) => {
    // do something with response
}).catch((error) => {
    // catch and handle errors
});

運用環境では、ほとんどの場合、トークン キャッシュをシリアル化して保持する必要があります。 アプリケーションの種類に応じて、次のことができます。

  • デスクトップ アプリ、コンソール アプリ (パブリック クライアント アプリ (PCA)):
    • MSAL ノード拡張機能を使用します。これは、Windows、Linux、Mac OS 上の保存時の永続化と暗号化ソリューションを提供します
  • Web アプリ、Web API、デーモン アプリ (機密クライアント アプリ (CCA)):
    • MSAL のインメモリ トークン キャッシュは、運用環境ではスケーリングされません。 分散トークン キャッシュ パターンを使用して、選択したストレージ環境 (Redis、MongoDB、SQL データベースなど) にキャッシュを永続化 -keep、Redis のようなメモリ キャッシュを永続性の第 1 レイヤーとして、SQL データベースを 2 つ目の安定した永続化レイヤーとして同時に使用できることを念頭に置きます。

メモリ内キャッシュ

MSAL はメモリ内キャッシュを維持します。 メモリ内キャッシュは、アプリケーション キャッシュの状態を代表します。 メモリ内キャッシュの有効期間は、MSAL アプリケーション オブジェクトと同じです。 MSAL を使用するプロセスが再起動すると、プロセスのライフサイクルが完了するとキャッシュは消去されます。 メモリ内キャッシュが空で、キャッシュを復元する永続的なキャッシュがない場合、ユーザーは再認証する必要があります。 この場合、ユーザーがまだ Microsoft Entra ID とのアクティブなセッションを持っている場合は、プロンプトなしで再認証される可能性があります。ただし、これによりユーザー エクスペリエンスが低下します。 サービス間のシナリオ (つまり、クライアント資格情報フロー、代理フロー) も、Microsoft Entra IDからトークンを取得するには HTTP 要求が必要であり、キャッシュからトークンを取得するよりもはるかに低速であるため、問題が発生します。

メモリ内キャッシュはサーバー側アプリケーションではスケーラブルではなく、キャッシュに数 100 個のトークンを保持した後にパフォーマンスが低下することに注意してください。 Web アプリと Web API のシナリオでは、これは約 100 人のユーザーにサービスを提供します。 他のアプリを呼び出すためにクライアント資格情報付与を使用するデーモン アプリのシナリオでは、これは数 100 のテナントを意味します。 詳細については、以下の パフォーマンス を参照してください。

⚠️ セキュリティと望ましいキャッシュの寿命の両方のために、すべての運用アプリケーションの暗号化を使用してキャッシュを保持することをお勧めします。 キャッシュを保持しないことを選択した場合でも、 TokenCache インターフェイスを使用してキャッシュされたエンティティにアクセスできます。

永続的キャッシュ

MSAL ノードは、メモリ内キャッシュにアクセスするとイベントを発生させ、アプリはキャッシュを保持するかどうかを選択できます ( TokenCacheContext を参照) (ファイル、SQL データベースなど)。 これは、次の 2 つのアクションを構成します。

  1. キャッシュにアクセスする前に、永続ストレージから MSAL のメモリ上にキャッシュを読み込む
  2. メモリ内キャッシュが前回のアクセス以降に変更された場合は、キャッシュを永続化に戻します

キャッシュを永続化するために、MSAL は 構成でカスタム キャッシュ プラグインを受け入れます。 このプラグインは 、ICachePlugin インターフェイスを実装する必要があります。

interface ICachePlugin {
    beforeCacheAccess: (tokenCacheContext: TokenCacheContext) => Promise<void>;
    afterCacheAccess: (tokenCacheContext: TokenCacheContext) => Promise<void>;
}

ICachePlugin インターフェイスの基本的な実装は次のようになります (サーバー側アプリを構築する場合は、パフォーマンスとセキュリティも参照してください)。

class MyCachePlugin implements ICachePlugin {
    private client: ICacheClient;

    constructor(client: ICacheClient) {
        this.client = client; // client object to access the persistent cache
    }

    public async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
        const cacheData = await this.client.get(); // get the cache from persistence
        cacheContext.tokenCache.deserialize(cacheData); // deserialize it to in-memory cache
    }

    public async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
        if (cacheContext.cacheHasChanged) {
            await this.client.set(cacheContext.tokenCache.serialize()); // deserialize in-memory cache to persistence
        }
    }
}
  • パブリック クライアント アプリを開発している場合、 MSAL Node Extensions はこれを自動的に処理します。
  • 機密クライアント アプリを開発している場合は、サーバーごとの単一のキャッシュ インスタンスは、多数の サーバー とアプリ インスタンスを持つクラウド環境には適していないため、別のサービスを介してキャッシュを保持する必要があります。

トークン キャッシュは、ディスクに保存するときに暗号化することを強くお勧めします。 パブリック クライアント アプリの場合、これは MSAL ノード拡張機能と共にすぐに使用できます。 ただし、機密クライアントの場合は、適切な暗号化ソリューションを考案する責任があります。

パフォーマンスとセキュリティ

パブリック クライアント アプリでは、MSAL Node Extensions によってパフォーマンスとセキュリティが保証されます。

ユーザーを処理する機密クライアント アプリ (ユーザーをサインインして Web API を呼び出す Web アプリ、ダウンストリーム Web API を呼び出す Web API) では、特定のアプリケーションに対して多数のユーザーが同時にアクティブになる可能性があります。 ユーザーごとに 1 つのキャッシュ BLOB ( CacheRecord を参照) をシリアル化することをお勧めします。 これは、分散システム全体でキャッシュをスケーリングするのに役立ちます。 キャッシュをパーティション分割するためのキー (つまりパーティション キー) を使用します。次に例を示します。

  • Web アプリの場合: <userObjectId>.<tenantId> (つまり、 homeAccountId)
  • クライアント資格情報付与を使用するマルチテナント デーモン アプリの場合: <clientId>.<tenantId>
  • OBO を使用して他の Web API を呼び出す Web API の場合: 受信したアクセス トークンのハッシュ (つまり oboAssertion) - その後 OBO トークンと交換されることになるトークン

⚠️ 使用状況を監視し 、パフォーマンス の低下を回避する方法の詳細については、パフォーマンスを確認してください。

Web アプリ

Web アプリはユーザー向けであり、多くの場合、各ユーザーを追跡するためにセッションに依存するため、キャッシュに適したパーティション キーは多くの場合、セッション データ内に格納され、キャッシュ参照を実行する前に取得する必要があります。 これを支援するために、MSAL Node は 、ICachePlugin を実装する DistributedCachePlugin クラスを提供します。 DistributedCachePluginのインスタンスには、次のものが必要です。

  • 永続化サーバー (Redis、MySQL など) に操作と操作を実装するget (set)。
  • 特定のセッション ID に関してキャッシュに対する読み取りと書き込みを行うパーティション マネージャー (IPartitionManager)。

サンプル実装については、 DistributedCachePlugin を使用した Web アプリ を参照してください。

こちらも参照ください

MSAL Node アプリでのキャッシュの処理方法の詳細については、以下のサンプルを参照してください。