@equinor/fusion-framework-module-msal-node
Version:
Fusion Framework module for secure Azure AD authentication in Node.js using MSAL. Supports interactive, silent, and token-only authentication modes with encrypted token storage.
97 lines (90 loc) • 4.04 kB
text/typescript
import { tmpdir } from 'node:os';
import path from 'node:path';
const importMsalNodeExtensions = async (): Promise<
typeof import('@azure/msal-node-extensions')
> => {
try {
return await import('@azure/msal-node-extensions');
} catch (cause) {
throw new Error(
'Failed to load @azure/msal-node-extensions. ' +
'Token cache persistence requires a native module (keytar/libsecret) that is only ' +
'available in interactive desktop environments. Install the optional dependency or ' +
'use a non-caching auth mode.',
{ cause },
);
}
};
/**
* Resolves the directory path for storing the authentication cache.
*
* Uses the user's root directory if available, otherwise falls back to the OS temp directory.
*
* @returns The resolved cache directory path as a string.
*/
const resolveCachePath = async (): Promise<string> => {
const { Environment } = await importMsalNodeExtensions();
return Environment?.getUserRootDirectory() ?? tmpdir();
};
/**
* Resolves the file path for the authentication cache based on tenant and client IDs.
*
* @param tenantId - The Azure AD tenant ID.
* @param clientId - The Azure AD client/application ID.
* @returns The full file path for the cache file.
*/
const resolveCacheFilePath = async (tenantId: string, clientId: string): Promise<string> => {
return path.join(await resolveCachePath(), `.token-cache-${tenantId}_${clientId}`);
};
/**
* Creates a persistence cache for storing authentication data securely on disk.
*
* The cache is encrypted and scoped to the current user for security. It is uniquely identified
* by the provided tenant and client IDs, and is associated with the 'fusion-framework' service.
*
* Requires `@azure/msal-node-extensions` to be installed (optional dependency).
* Only available in interactive desktop environments with a system keychain.
*
* @param tenantId - The Azure AD tenant ID used to identify the cache.
* @param clientId - The Azure AD client/application ID used to identify the cache.
* @returns A promise that resolves to the created persistence cache instance.
*/
export const createPersistenceCache = async (tenantId: string, clientId: string) => {
const { DataProtectionScope, PersistenceCreator } = await importMsalNodeExtensions();
return PersistenceCreator.createPersistence({
cachePath: await resolveCacheFilePath(tenantId, clientId),
serviceName: 'fusion-framework',
accountName: [tenantId, clientId].join('_'),
dataProtectionScope: DataProtectionScope.CurrentUser,
});
};
/**
* Clears the persistence cache for a specific tenant and client.
*
* Deletes the cache file and all associated authentication data for the given tenant and client IDs.
*
* @param tenantId - The Azure AD tenant ID.
* @param clientId - The Azure AD client/application ID.
* @returns A promise that resolves when the cache has been successfully cleared.
*/
// Deliberately co-located with `createPersistenceCache` above, which it wraps
// fusion-lint-disable-next-line single-export-per-file
export const clearPersistenceCache = async (tenantId: string, clientId: string): Promise<void> => {
const cache = await createPersistenceCache(tenantId, clientId);
await cache.delete();
};
/**
* Creates a `PersistenceCachePlugin` instance for use with MSAL, using the provided tenant and client IDs.
*
* This plugin enables MSAL to use the secure persistence cache for token storage.
*
* @param tenantId - The Azure AD tenant ID.
* @param clientId - The Azure AD client/application ID.
* @returns A promise that resolves to an instance of `PersistenceCachePlugin`.
*/
// Deliberately co-located with `createPersistenceCache` above, which it wraps
// fusion-lint-disable-next-line single-export-per-file
export const createPersistenceCachePlugin = async (tenantId: string, clientId: string) => {
const { PersistenceCachePlugin } = await importMsalNodeExtensions();
return new PersistenceCachePlugin(await createPersistenceCache(tenantId, clientId));
};