src/core/swa-cli-persistence-plugin/persistence-cache-plugin.ts (44 lines of code) (raw):
import { TokenCachePersistenceOptions } from "@azure/identity";
import { ICachePlugin, TokenCacheContext } from "@azure/msal-common";
import { logger } from "../utils/logger.js";
import { Environment } from "./impl/azure-environment.js";
import { NativeCredentialsStore } from "./impl/credentials-store.js";
import { CryptoService } from "./impl/crypto.js";
import { getMachineId } from "./impl/machine-identifier.js";
import { SecretStorage } from "./impl/secret-storage.js";
export interface SWACLIPersistenceCacheOptions {
enableCache: boolean;
clearCache: boolean;
}
/**
* SWA CLI cache plugin which enables callers to write the MSAL cache to disk on Windows,
* macOs, and Linux using native keychain.
*/
export class SWACLIPersistenceCachePlugin implements ICachePlugin {
constructor(private options: TokenCachePersistenceOptions) {}
/**
* Reads from storage and saves an in-memory copy. If keychain has not been updated
* since last time data was read, in memory copy is used.
*
* If cacheContext.cacheHasChanged === true, then file lock is created and not deleted until
* afterCacheAccess() is called, to prevent the cache file from changing in between
* beforeCacheAccess() and afterCacheAccess().
*/
public async beforeCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
logger.silly(`Executing before cache access plugin`);
const machineId = await getMachineId();
logger.silly(`Machine ID: ${machineId ? "<hidden>" : "<empty>"}`);
const secretStorage = new SecretStorage(this.options, new NativeCredentialsStore(this.options), new CryptoService(machineId));
const cachedValue: string | undefined = await secretStorage.getCredentials(machineId, Environment.AzureCloud.name);
logger.silly(`Credentials: ${cachedValue ? "<hidden>" : "<empty>"}`);
cachedValue && cacheContext.tokenCache.deserialize(cachedValue);
logger.silly(`Before cache access plugin. Done.`);
}
/**
* Writes to storage if MSAL in memory copy of cache has been changed.
*/
public async afterCacheAccess(cacheContext: TokenCacheContext): Promise<void> {
logger.silly(`Executing after cache access plugin`);
const machineId = await getMachineId();
logger.silly(`Machine ID: ${machineId ? "<hidden>" : "<empty>"}`);
const secretStorage = new SecretStorage(this.options, new NativeCredentialsStore(this.options), new CryptoService(machineId));
logger.silly(`Did TokenCacheContext cache changed: ${cacheContext.cacheHasChanged}`);
if (cacheContext.cacheHasChanged) {
await secretStorage.setCredentials(machineId, Environment.AzureCloud.name, cacheContext.tokenCache.serialize());
}
logger.silly(`After cache access plugin. Done.`);
}
/**
* Clears credentials cache.
*/
public async clearCache(): Promise<void> {
logger.silly(`Clearing credentials cache`);
const machineId = await getMachineId();
logger.silly(`Machine ID: ${machineId ? "<hidden>" : "<empty>"}`);
const secretStorage = new SecretStorage(this.options, new NativeCredentialsStore(this.options), new CryptoService(machineId));
await secretStorage.deleteCredentials(machineId, Environment.AzureCloud.name);
logger.silly(`Credentials cache cleared`);
}
}