or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication-flows.mdconfidential-client.mdconfiguration.mderror-handling.mdindex.mdmanaged-identity.mdpublic-client.mdtoken-cache.md
tile.json

token-cache.mddocs/

Token Cache Management

MSAL Node provides a comprehensive token caching system that stores authentication artifacts in memory with support for serialization, distributed caching, and custom cache plugins. The cache system improves performance by avoiding unnecessary network requests and enables silent token acquisition.

Capabilities

TokenCache Class

Main token cache implementation providing in-memory storage with serialization support.

/**
 * Token cache interface for basic cache operations
 */
interface ITokenCache {
  /** Get all cached accounts */
  getAllAccounts(): Promise<AccountInfo[]>;
  /** Get account by home account ID */
  getAccountByHomeId(homeAccountId: string): Promise<AccountInfo | null>;
  /** Get account by local account ID */
  getAccountByLocalId(localAccountId: string): Promise<AccountInfo | null>;
  /** Remove account and associated tokens from cache */
  removeAccount(account: AccountInfo): Promise<void>;
}

/**
 * Main token cache class with serialization support
 */
class TokenCache implements ITokenCache, ISerializableTokenCache {
  /** Get all cached accounts */
  getAllAccounts(): Promise<AccountInfo[]>;
  
  /** Get account by home account ID */
  getAccountByHomeId(homeAccountId: string): Promise<AccountInfo | null>;
  
  /** Get account by local account ID */
  getAccountByLocalId(localAccountId: string): Promise<AccountInfo | null>;
  
  /** Remove account and associated tokens from cache */
  removeAccount(account: AccountInfo): Promise<void>;
  
  /** Serialize cache to JSON string */
  serialize(): Promise<string>;
  
  /** Deserialize cache from JSON string */
  deserialize(cache: string): Promise<void>;
  
  /** Get all cached access tokens */
  getAllAccessTokens(): Promise<AccessTokenEntity[]>;
  
  /** Get all cached refresh tokens */
  getAllRefreshTokens(): Promise<RefreshTokenEntity[]>;
  
  /** Get all cached ID tokens */
  getAllIdTokens(): Promise<IdTokenEntity[]>;
  
  /** Clear all cache entries */
  clear(): Promise<void>;
}

Usage Example:

import { PublicClientApplication } from "@azure/msal-node";

const pca = new PublicClientApplication({
  auth: {
    clientId: "your-client-id"
  }
});

// Get token cache instance
const cache = pca.getTokenCache();

// Get all accounts
const accounts = await cache.getAllAccounts();
console.log("Cached accounts:", accounts.length);

// Get specific account
if (accounts.length > 0) {
  const account = await cache.getAccountByHomeId(accounts[0].homeAccountId);
  console.log("Account:", account?.username);
}

// Remove account from cache
if (accounts.length > 0) {
  await cache.removeAccount(accounts[0]);
  console.log("Account removed from cache");
}

Cache Serialization

Serialize and deserialize cache for persistent storage.

/**
 * Interface for serializable token cache
 */
interface ISerializableTokenCache {
  /** Serialize cache to JSON string */
  serialize(): Promise<string>;
  /** Deserialize cache from JSON string */
  deserialize(cache: string): Promise<void>;
}

/**
 * Cache serialization types
 */
type JsonCache = {
  /** Account entities indexed by key */
  Account: Record<string, SerializedAccountEntity>;
  /** ID token entities indexed by key */
  IdToken: Record<string, SerializedIdTokenEntity>;
  /** Access token entities indexed by key */
  AccessToken: Record<string, SerializedAccessTokenEntity>;
  /** Refresh token entities indexed by key */
  RefreshToken: Record<string, SerializedRefreshTokenEntity>;
  /** App metadata entities indexed by key */
  AppMetadata: Record<string, SerializedAppMetadataEntity>;
};

/**
 * In-memory cache representation
 */
type InMemoryCache = {
  /** Account entities */
  accounts: Record<string, AccountEntity>;
  /** ID token entities */
  idTokens: Record<string, IdTokenEntity>;
  /** Access token entities */
  accessTokens: Record<string, AccessTokenEntity>;
  /** Refresh token entities */
  refreshTokens: Record<string, RefreshTokenEntity>;
  /** App metadata entities */
  appMetadata: Record<string, AppMetadataEntity>;
};

/**
 * Key-value store for cache operations
 */
type CacheKVStore = Record<string, ValidCacheType>;

/**
 * Valid cache value types
 */
type ValidCacheType = 
  | AccountEntity 
  | IdTokenEntity 
  | AccessTokenEntity 
  | RefreshTokenEntity 
  | AppMetadataEntity;

Usage Example:

import fs from "fs/promises";

const cache = pca.getTokenCache();

// Serialize cache to file
const serializedCache = await cache.serialize();
await fs.writeFile("./token-cache.json", serializedCache);
console.log("Cache saved to file");

// Load cache from file
try {
  const cacheData = await fs.readFile("./token-cache.json", "utf8");
  await cache.deserialize(cacheData);
  console.log("Cache loaded from file");
} catch (error) {
  console.log("No existing cache file found");
}

// Clear cache
await cache.clear();
console.log("Cache cleared");

Serialized Entity Types

Detailed structure of cached entities for serialization.

/**
 * Serialized account entity
 */
type SerializedAccountEntity = {
  /** Home account ID (user identifier across tenants) */
  home_account_id: string;
  /** Environment (authority host) */
  environment: string;
  /** Realm (tenant ID) */
  realm: string;
  /** Local account ID (user identifier within tenant) */
  local_account_id: string;
  /** Username (UPN or email) */
  username: string;
  /** Account type */
  authority_type: string;
  /** Display name */
  name?: string;
  /** Last modification timestamp */
  last_modification_time?: string;
  /** Additional client info */
  client_info?: string;
};

/**
 * Serialized ID token entity
 */
type SerializedIdTokenEntity = {
  /** Home account ID */
  home_account_id: string;
  /** Environment */
  environment: string;
  /** Credential type */
  credential_type: string;
  /** Client ID */
  client_id: string;
  /** ID token JWT */
  secret: string;
  /** Realm */
  realm: string;
};

/**
 * Serialized access token entity
 */
type SerializedAccessTokenEntity = {
  /** Home account ID */
  home_account_id: string;
  /** Environment */
  environment: string;
  /** Credential type */
  credential_type: string;
  /** Client ID */
  client_id: string;
  /** Access token */
  secret: string;
  /** Realm */
  realm: string;
  /** Target scopes */
  target: string;
  /** Expiration timestamp */
  expires_on: string;
  /** Extended expiration timestamp */
  extended_expires_on?: string;
  /** Cached at timestamp */
  cached_at: string;
  /** Token type (Bearer) */
  token_type?: string;
  /** Key ID for key-based tokens */
  key_id?: string;
};

/**
 * Serialized refresh token entity
 */
type SerializedRefreshTokenEntity = {
  /** Home account ID */
  home_account_id: string;
  /** Environment */
  environment: string;
  /** Credential type */
  credential_type: string;
  /** Client ID */
  client_id: string;
  /** Refresh token */
  secret: string;
  /** Family ID for family of client IDs */
  family_id?: string;
  /** Target scopes */
  target?: string;
  /** Realm */
  realm?: string;
};

/**
 * Serialized app metadata entity
 */
type SerializedAppMetadataEntity = {
  /** Client ID */
  client_id: string;
  /** Environment */
  environment: string;
  /** Family ID */
  family_id?: string;
};

Cache Plugin System

Pluggable cache system for custom storage backends.

/**
 * Cache plugin interface for custom cache implementations
 */
interface ICachePlugin {
  /** Called before cache access */
  beforeCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void>;
  /** Called after cache access */
  afterCacheAccess(tokenCacheContext: TokenCacheContext): Promise<void>;
}

/**
 * Token cache context provided to cache plugins
 */
type TokenCacheContext = {
  /** Token cache instance */
  tokenCache: ISerializableTokenCache;
  /** Whether cache has changed */
  hasChanged: boolean;
  /** Client ID of the application */
  clientId: string;
  /** Account information if available */
  account?: AccountInfo;
  /** Requested scopes */
  scopes?: string[];
};

/**
 * Cache options for configuring cache behavior
 */
type CacheOptions = {
  /** Cache plugin for custom storage */
  cachePlugin?: ICachePlugin;
  /** 
   * @deprecated claims-based-caching functionality will be removed in the next version
   */
  claimsBasedCachingEnabled?: boolean;
};

Usage Example:

import fs from "fs/promises";

// Custom file-based cache plugin
class FileCachePlugin implements ICachePlugin {
  private cacheFilePath = "./msal-cache.json";

  async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
    try {
      const cacheData = await fs.readFile(this.cacheFilePath, "utf8");
      await cacheContext.tokenCache.deserialize(cacheData);
    } catch (error) {
      // Cache file doesn't exist yet
      console.log("No existing cache file found");
    }
  }

  async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
    if (cacheContext.hasChanged) {
      const serializedCache = await cacheContext.tokenCache.serialize();
      await fs.writeFile(this.cacheFilePath, serializedCache);
      console.log("Cache updated and saved to file");
    }
  }
}

// Use custom cache plugin
const pca = new PublicClientApplication({
  auth: {
    clientId: "your-client-id"
  },
  cache: {
    cachePlugin: new FileCachePlugin()
  }
});

Distributed Cache Support

Distributed cache plugin for Redis and other external cache systems.

/**
 * Interface for external cache clients (Redis, etc.)
 */
interface ICacheClient {
  /** Retrieve value from cache using key */
  get(key: string): Promise<string>;
  /** Save value to cache using key */
  set(key: string, value: string): Promise<string>;
}

/**
 * Interface for cache partitioning strategies
 */
interface IPartitionManager {
  /** Get partition key for current context */
  getKey(): Promise<string>;
  /** Extract partition key from account entity */
  extractKey(accountEntity: AccountEntity): Promise<string>;
}

/**
 * Distributed cache plugin for external cache systems like Redis, DynamoDB, etc.
 */
class DistributedCachePlugin implements ICachePlugin {
  constructor(client: ICacheClient, partitionManager: IPartitionManager);
  
  /** Called before cache access to deserialize from external cache */
  beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void>;
  
  /** Called after cache access to serialize to external cache */
  afterCacheAccess(cacheContext: TokenCacheContext): Promise<void>;
}

Usage Example:

import Redis from "ioredis";

// Redis cache client implementation
class RedisCacheClient implements ICacheClient {
  private redis: Redis;

  constructor(redisUrl: string) {
    this.redis = new Redis(redisUrl);
  }

  async get(key: string): Promise<string | null> {
    return await this.redis.get(key);
  }

  async set(key: string, value: string): Promise<void> {
    await this.redis.set(key, value, "EX", 3600); // 1 hour expiration
  }
}

// Simple partition manager
class SimplePartitionManager implements IPartitionManager {
  private clientId: string;

  constructor(clientId: string) {
    this.clientId = clientId;
  }

  getKey(): string {
    return `msal-cache:${this.clientId}`;
  }

  extractKey(accountEntity: AccountEntity): string {
    return `msal-cache:${this.clientId}:${accountEntity.homeAccountId}`;
  }
}

// Use distributed cache
const redisClient = new RedisCacheClient("redis://localhost:6379");
const partitionManager = new SimplePartitionManager("your-client-id");
const distributedCachePlugin = new DistributedCachePlugin(redisClient, partitionManager);

const pca = new PublicClientApplication({
  auth: {
    clientId: "your-client-id"
  },
  cache: {
    cachePlugin: distributedCachePlugin
  }
});

Account Management

Advanced account management operations.

/**
 * Account information structure
 */
type AccountInfo = {
  /** Home account ID (unique across tenants) */
  homeAccountId: string;
  /** Environment (authority host) */
  environment: string;
  /** Tenant ID */
  tenantId: string;
  /** Username (UPN or email) */
  username: string;
  /** Local account ID (unique within tenant) */
  localAccountId: string;
  /** Display name */
  name?: string;
  /** ID token claims */
  idTokenClaims?: IdTokenClaims;
};

/**
 * ID token claims structure
 */
type IdTokenClaims = {
  /** Issuer */
  iss?: string;
  /** Subject (user ID) */
  sub?: string;
  /** Audience */
  aud?: string;
  /** Expiration time */
  exp?: number;
  /** Issued at time */
  iat?: number;
  /** Authentication time */
  auth_time?: number;
  /** Nonce */
  nonce?: string;
  /** Preferred username */
  preferred_username?: string;
  /** Name */
  name?: string;
  /** Email */
  email?: string;
  /** Object ID */
  oid?: string;
  /** Tenant ID */
  tid?: string;
  /** Version */
  ver?: string;
};

Usage Example:

// Account management operations
const accounts = await cache.getAllAccounts();

// Filter accounts by tenant
const tenantAccounts = accounts.filter(account => 
  account.tenantId === "specific-tenant-id"
);

// Find account by username
const userAccount = accounts.find(account => 
  account.username === "user@domain.com"
);

// Get detailed account information
if (userAccount) {
  console.log("Account details:");
  console.log("- Home Account ID:", userAccount.homeAccountId);
  console.log("- Tenant ID:", userAccount.tenantId);
  console.log("- Username:", userAccount.username);
  console.log("- Display Name:", userAccount.name);
  
  if (userAccount.idTokenClaims) {
    console.log("- Email:", userAccount.idTokenClaims.email);
    console.log("- Object ID:", userAccount.idTokenClaims.oid);
  }
}

// Remove specific account
if (userAccount) {
  await cache.removeAccount(userAccount);
  console.log("Account removed from cache");
}

Cache Performance Optimization

Best practices for cache performance and management.

/**
 * Cache performance considerations
 */
type CachePerformanceOptions = {
  /** Enable cache compression */
  enableCompression?: boolean;
  /** Cache size limits */
  maxCacheSize?: number;
  /** Token expiration buffer */
  expirationBuffer?: number;
  /** Cleanup interval */
  cleanupInterval?: number;
};

Usage Example:

// High-performance cache plugin with compression and cleanup
class OptimizedCachePlugin implements ICachePlugin {
  private cacheFile = "./optimized-cache.json";
  private lastCleanup = Date.now();
  private cleanupInterval = 60000; // 1 minute

  async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
    // Periodic cleanup of expired tokens
    if (Date.now() - this.lastCleanup > this.cleanupInterval) {
      await this.cleanupExpiredTokens(cacheContext);
      this.lastCleanup = Date.now();
    }

    // Load cache from file
    try {
      const cacheData = await fs.readFile(this.cacheFile, "utf8");
      await cacheContext.tokenCache.deserialize(cacheData);
    } catch (error) {
      // No cache file exists
    }
  }

  async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
    if (cacheContext.hasChanged) {
      const serializedCache = await cacheContext.tokenCache.serialize();
      
      // Compress cache data (optional)
      const compressedCache = this.compressCache(serializedCache);
      
      await fs.writeFile(this.cacheFile, compressedCache);
    }
  }

  private async cleanupExpiredTokens(cacheContext: TokenCacheContext): Promise<void> {
    // Implementation to remove expired access tokens
    const accessTokens = await cacheContext.tokenCache.getAllAccessTokens();
    const now = Date.now() / 1000;

    for (const token of accessTokens) {
      if (token.expiresOn && parseInt(token.expiresOn) < now) {
        // Remove expired token
        console.log("Removing expired token");
      }
    }
  }

  private compressCache(cache: string): string {
    // Optional: implement compression
    return cache;
  }
}

Cache Storage Patterns

In-Memory Only

// Default behavior - cache only exists in memory
const pca = new PublicClientApplication({
  auth: { clientId: "your-client-id" }
  // No cache plugin = in-memory only
});

File-Based Persistence

// File-based cache for desktop applications
const pca = new PublicClientApplication({
  auth: { clientId: "your-client-id" },
  cache: { cachePlugin: new FileCachePlugin() }
});

Distributed Cache

// Redis-based cache for scalable web applications
const pca = new PublicClientApplication({
  auth: { clientId: "your-client-id" },
  cache: { cachePlugin: new DistributedCachePlugin(redisClient, partitionManager) }
});

Encrypted Cache

// Encrypted file cache for sensitive environments
const pca = new PublicClientApplication({
  auth: { clientId: "your-client-id" },
  cache: { cachePlugin: new EncryptedFileCachePlugin(encryptionKey) }
});