Microsoft Node 的身份验证扩展

Node 的 Microsoft 身份验证扩展为客户端应用程序提供安全机制,用于执行跨平台令牌缓存序列化和持久性。

MSAL Node 要求开发人员实现自己的逻辑来保存令牌缓存。 MSAL Node 扩展旨在为公共客户端应用程序(桌面客户端、CLI 应用程序等)提供跨 Windows、Mac 和 Linux 的可靠、安全且可配置的令牌缓存坚持性实现。 它提供了一种机制,用于同时通过多个进程加密和访问令牌缓存。

支持的平台Windows、Mac 和 Linux:

  • Windows - DPAPI 用于加密。
  • MAC - MAC 密钥链通过 npm keytar 使用。
  • Linux - LibSecret 用于通过 npm keytar 将内容存储在“Secret Service”中。

Code

创建持久性层

用于创建持久性层的 API 将因目标平台而异。

或者,可以使用 createPersistencePersistenceCreator 中的 API 作为泛型包装器,并根据平台/OS 选择适当的持久性方法。

const { PublicClientApplication } = require("@azure/msal-node");
const {
  DataProtectionScope,
  PersistenceCreator,
  PersistenceCachePlugin,
} = require("@azure/msal-node-extensions");

const persistence = await PersistenceCreator.createPersistence({
                cachePath: "path/to/cache/file.json",
                dataProtectionScope: DataProtectionScope.CurrentUser,
                serviceName: "test-msal-electron-service",
                accountName: "test-msal-electron-account",
                usePlaintextFileOnLinux: false,
          });
// Use the persistence object to initialize an MSAL PublicClientApplication with cachePlugin
const pca = new PublicClientApplication({
                auth: {
                        clientId: "CLIENT_ID_HERE",
                    },
                cache: {
                        cachePlugin: new PersistenceCachePlugin(persistence);
                    },
                });

或者,可以使用以下特定于平台的选项:


const { FilePersistenceWithDataProtection, DataProtectionScope } = require("@azure/msal-node-extensions");
const { PublicClientApplication } = require("@azure/msal-node");

const cachePath = "path/to/cache/file.json";
const dataProtectionScope = DataProtectionScope.CurrentUser;
const optionalEntropy = ""; //specifies password or other additional entropy used to encrypt the data.
const windowsPersistence = await FilePersistenceWithDataProtection.create(cachePath, dataProtectionScope, optionalEntropy);
// Use the persistence object to initialize an MSAL PublicClientApplication with cachePlugin
const pca = new PublicClientApplication({
                auth: {
                        clientId: "CLIENT_ID_HERE",
                    },
                cache: {
                        cachePlugin: new PersistenceCachePlugin(windowsPersistence);
                    },
                });

  • cachePath 是将存储加密缓存文件的文件系统中的路径。
  • dataProtectionScope 指定数据保护的范围,即当前用户或本地计算机。 不需要密钥来保护或取消保护数据。 如果将范围设置为 CurrentUser,则只有凭据上运行的应用程序才能取消保护数据;但是,这意味着在凭据上运行的任何应用程序都可以访问受保护的数据。 如果将范围设置为 LocalMachine,则计算机上的任何完全信任应用程序都可以取消保护、访问和修改数据。
  • optionalEntropy 指定用于加密数据的密码或其他附加信息量。

FilePersistenceWithDataProtection 使用 Win32 CryptProtectData 和 CryptUnprotectData API。 有关 dataProtectionScope 或 optionalEntropy 的详细信息,请参阅这些 API 的文档。

所有平台

为了方便起见,提供了适用于所有平台的未加密文件持久化方式,但不推荐使用该方式。

const { FilePersistence } = require("@azure/msal-node-extensions");

const filePath = "path/to/cache/file.json";
const filePersistence = await FilePersistence.create(filePath, loggerOptions);
// Pass the persistence to msal config's cachePlugin
const pca = new PublicClientApplication({
    auth: {
            clientId: "CLIENT_ID_HERE",
        },
    cache: {
            cachePlugin: new PersistenceCachePlugin(filePersistence);
        },
  });

如果未创建文件或目录, FilePersistence.create() 则会以递归方式创建该文件和路径中的任何目录。 这可以在FilePersistence.ts中看到其实际应用

将锁定选项传递到缓存插件以实现并发

通过传入上一步创建的持久性对象,创建 PersistenceCachePlugin

const { PersistenceCachePlugin } = require("@azure/msal-node-extensions");

const persistenceCachePlugin = new PersistenceCachePlugin(windowsPersistence); // or any of the other ones.

为了支持多个进程的并发访问,扩展使用基于文件的锁。 可以配置通过 CrossPlatformLockOptions 获取锁时的重试次数和重试延迟。

const {
  PersistenceCreator,
  PersistenceCachePlugin,
} = require("@azure/msal-node-extensions");

const lockOptions = {
    retryNumber: 100,
    retryDelay: 50
}

const persistence = await PersistenceCreator.createPersistence(persistenceConfiguration);
const persistenceCachePlugin = new PersistenceCachePlugin(persistence, lockOptions); // or any of the other ones
const pca = new PublicClientApplication({
    auth: {
            clientId: "CLIENT_ID_HERE",
        },
    cache: {
            cachePlugin: persistenceCachePlugin
        },
    });

在 MSAL 节点 PublicClientApplication 配置上设置 PersistenceCachePlugin (示例)

总之,一旦有了 PersistenceCachePlugin,就可以将其设置到 MSAL Node PublicClientApplication 上,方法是按如下所示将其作为 配置 对象的一部分进行设置。

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

const publicClientConfig = {
    auth: {
        clientId: "",
        authority: "",
    },
    cache: {
        cachePlugin: persistenceCachePlugin
    },
};

const pca = new PublicClientApplication(publicClientConfig);

示例(适用于 Electron node-js 桌面应用):-

authConfig.js:-

const AAD_ENDPOINT_HOST = "https://login.microsoftonline.com/"; // include the trailing slash
const REDIRECT_URI = "ENTER_REDIRECT_URI";

const cachePath = "path/to/cache/file.json";

/*define persistence config based on the appropriate persistence you are using(e.g- FilePersistenceWithDataProtection, generic PersistenceCreateor, etc)*/

//defining persistence config for PersistenceCreator
const persistenceConfiguration = {
    cachePath,
    dataProtectionScope: DataProtectionScope.CurrentUser,
    serviceName: "test-msal-electron-service",
    accountName: "test-msal-electron-account",
    usePlaintextFileOnLinux: false,
}

  const msalConfig = {
    auth: {
        clientId: "CLIENT_ID_HERE",
        authority: `${AAD_ENDPOINT_HOST}TENANT_ID_HERE`,
    },
    cache: {
        cachePlugin: null // set later in main.js as shown above 
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: LogLevel.Verbose,
        },
    },
};
...

module.exports = {
  msalConfig: msalConfig,
  protectedResources: protectedResources,
  REDIRECT_URI: REDIRECT_URI,
  persistenceConfiguration
};

电子开发人员注意事项

Electron 示例:此 示例 演示如何将 msal-node-extensions 库集成到经过 webpack 打包的 Electron 应用程序中。

如果要将此扩展用于 Electron,则可能会遇到类似于下面的错误:

Uncaught Exception:
Error: The module
"<path-to-project>\node_modules\...\dpapi.node" was compiled against a different Node.js version using NODE_MODULE_VERSION 85. This version of Node.js requires NODE_MODULE_VERSION 80. Please try re-compiling or re-installing the module...."

此错误可能是由于电子项目和扩展之间的 Node.js 版本差异。 可通过以下步骤重新生成包来处理此问题:

  • 如果尚未安装 electron-rebuild,请使用命令 npm i -D electron-rebuild 进行安装。
  • 如果存在,请从项目中移除packages-lock.json
  • ./node_modules/.bin/electron-rebuild运行

Samples

  1. Electron-webpack 持久化示例
  2. Msal-node 扩展示例