October 21st, 2021

Announcing the Azure Identity 2.0 client library and plugin packages for JavaScript

Will Temple
Software Engineer II

Announcing the Azure Identity 2.0 client library and plugin packages for JavaScript

For the last several months, the Azure SDK team has been building the next major iteration (version 2.0) of the Azure Identity client library for JavaScript (@azure/identity). Version 2.0 reached General Availability (GA) on October 15, 2021. As the cornerstone of our Azure Active Directory authentication/authorization workflow, this new major version of @azure/identity includes several improvements to the developer experience. Included are changes that reduce friction for developers who need to use @azure/identity without native machine-code (NMC) dependencies.

This article focuses on the new plugin API and packages. Our goals for this iteration of the Identity library required a breaking change and a new major version. We also made a handful of other improvements to enhance the developer experience of using @azure/identity while largely maintaining API compatibility with the previous version. Some improvements include:

  • A backend rework that uses the latest versions, features, and default settings of the Microsoft Authentication Library (MSAL).
  • InteractiveBrowserCredential now uses the Authorization Code Flow with Proof Key for Code Exchange (PKCE) by default. This change follows modern best practices for OAuth2/OpenID Connect authentication, instead of the implicit grant flow used in previous iterations.
  • The credentials’ constructors and getToken implementations now evaluate certain error cases differently, and no longer produce null, instead throwing errors in all error cases.

To migrate your app to version 2.0 of the Identity library, see the Migration guide.

Plugin packages

The previous iteration of the Identity library optionally depended on the keytar package. The package allowed access to the Azure account credentials stored in Visual Studio (VS) Code’s Azure Account extension. To support the goals for the next iteration of Identity, a new persistent token caching feature was introduced. This feature:

  • Enables caching of access tokens between process sessions in a secure store.
  • Required a dependency on @azure/msal-node-extensions, which relies on NMC to communicate with the Windows Data Protection API.

Several customers reported issues resulting from our dependence on these NMC modules, even “peer” or “optional” dependence. For example, some customers were unable or reasonably unwilling to build or use the modules because of security concerns. Sometimes security constraints in their CI/CD environments were to blame. Also, the newest versions of keytar provide prebuilt binaries for many of the most common runtime platforms; however, there are situations that cause the keytar installation to reject these prebuilt binaries and attempt to build itself. The @azure/msal-node-extensions package doesn’t provide prebuilt binaries at this time. A build is always required to use it. Building these packages requires a full C/C++ build toolchain, such as:

  • VS C++ build tools on Windows
  • XCode Command-Line tools on macOS
  • GCC/Clang and development headers on Linux

These requirements and the confusion around them added friction to the development process. The result was an unsatisfactory first-time experience with the @azure/identity package.

In the 2.0 release of @azure/identity, we’ve introduced a plugin API and two plugin packages. The new plugin architecture allows us to deliver an @azure/identity package that’s free of NMC dependencies by default—not even “optional” or “peer” dependencies. However, it also allows us to support use cases that rely on NMC implementations through plugin packages. The plugin packages encapsulate these NMC implementations, and can be used in tandem with the @azure/identity package to augment its functionality and include those features.

Developer Note: The plugin packages were originally classified as “extension” packages. After all, they “extend” the functionality of the @azure/identity package. Through UX studies and testing, we learned that this “extension” terminology could be confusing. Study participants often confused the @azure/identity-vscode package with an “extension” of VS Code itself. VS Code uses the same term to describe its marketplace extensions. As a result, we decided to use the term “plugin” instead, to reduce this confusion, especially as one of our plugin packages relates to VS Code!

Use a plugin package

All plugin packages follow the same basic structure. A new top-level function, named useIdentityPlugin, is exported from @azure/identity. Apps can use this function to add a plugin implementation to the @azure/identity package at runtime. The app code should call this function as early as possible. For example, in the module/script body immediately after importing the @azure/identity package. The following sample shows the initialization of the @azure/identity-vscode package (discussed further below):

import { vsCodePlugin } from "@azure/identity-vscode";
import { useIdentityPlugin, DefaultAzureCredential } from "@azure/identity";

// This function should be called once, as soon as possible in the execution flow of the app.
useIdentityPlugin(vsCodePlugin);

// Now, my app is augmented with the features of the VSCode plugin, which enables `DefaultAzureCredential` to
// search the VS Code Azure Account extension's token cache and use its session.

async function main() {
    const credential = new DefaultAzureCredential();

    // Use the credential to authenticate a service client from an Azure SDK package, such as `@azure/storage-blob`.
}

main().catch((error) => {
    console.error(error);
    process.exit(1);
})

@azure/identity-vscode

This plugin enables VisualStudioCodeCredential, which relies on keytar to search the VS Code Azure Account extension’s token cache and use its stored session. Without this plugin, VisualStudioCodeCredential instances will throw an error that directs the reader to install it. Since VisualStudioCodeCredential is part of the DefaultAzureCredential chain, this plugin also enables VS Code Azure Account authentication in DefaultAzureCredential instances. Previously, we only required that you install the keytar package. As such, this breaking change in @azure/identity 2.0 requires a change to your app code to continue using this credential type.

Before

import { VisualStudioCodeCredential } from "@azure/identity";

async function main() {
    const credential = new VisualStudioCodeCredential();

    // Somewhere in the execution of your app, `credential.getToken` will be called, and it will throw an error.
}

main().catch((error) => {
    console.error(error);
    process.exit(1);
});

With the previous iteration of the @azure/identity package, this app would execute as expected as long as the keytar package is available. In the new iteration, this app yields the following error message:

CredentialUnavailableError: No implementation of `VisualStudioCodeCredential` is available. You must install the identity-vscode plugin package (`npm install --save-dev @azure/identity-vscode`) and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling `useIdentityPlugin(vsCodePlugin)` before creating a `VisualStudioCodeCredential`.
    at VisualStudioCodeCredential.getToken (.../node_modules/@azure/identity/src/credentials/visualStudioCodeCredential.ts:184:13)

After

To resolve this error, follow the instructions in the error message by:

  1. Installing the @azure/identity-vscode package.
    npm install --save-dev @azure/identity-vscode
  2. Importing useIdentityPlugin from @azure/identity.
  3. Importing vsCodePlugin from @azure/identity-vscode.
  4. Calling useIdentityPlugin(vsCodePlugin).
import { vsCodePlugin } from "@azure/identity-vscode";
import { useIdentityPlugin, VisualStudioCodeCredential } from "@azure/identity";

useIdentityPlugin(vsCodePlugin);

async function main() {
    const credential = new VisualStudioCodeCredential();

    // Somewhere in the execution of your app, `credential.getToken` will be called, and it will now work as expected.
}

main().catch((error) => {
    console.error(error);
    process.exit(1);
});

@azure/identity-cache-persistence

A second plugin package was added that enables persistent (between process sessions) caching of access tokens. The plugin allows a credential to reuse tokens from a previous Node.js instance. As a result, the authentication flow isn’t repeated. The tokens are placed in a secure store that requires NMC to access. The store’s location and configuration is defined by the host operating system:

  • On Windows, it uses a file that is encrypted using the Windows Data Protection API (DPAPI).
  • On macOS, it uses the macOS Keychain.
  • On Linux, it uses libsecret, which will itself use a secure store such as the system keyring (depending on the system configuration).

This capability can reduce:

  • The number of times the authentication flow is required while using the app.
  • Excessive authentication requests when an app starts if a previous instance’s tokens can be reused.

Persistent token caching is an opt-in feature. It’s enabled by using the @azure/identity-cache-persistence plugin package:

import { useIdentityPlugin, DeviceCodeCredential } from "@azure/identity";
import { cachePersistencePlugin } from "@azure/identity-cache-persistence";

useIdentityPlugin(cachePersistencePlugin);

async function main() {
  // DeviceCodeCredential has an interactive authentication flow, so it will be useful to re-use a pre-existing token
  // if one exists. If we don't use token cache persistence, a developer/user will have to enter the code into a web
  // page once every time the app starts.
  const credential = new DeviceCodeCredential({
    tokenCachePersistenceOptions: {
      // This option must be set to `true` to enable the feature
      enabled: true,

      // If the following option is set to `true`, the access tokens may be stored in an UNSAFE, UNENCRYPTED file.
      // ONLY ENABLE THIS IF YOU'RE SURE YOU WANT IT.
      // unsafeAllowUnencryptedStorage: false,

      // The cache persistence plugin supports multiple namespaces. If you provide a `name` for the cache, it can only
      // use tokens from other credentials that used the same `name`.
      // name: "myCustomCacheName"
    }
  });
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

Note: If tokenCachePersistenceOptions are provided without installing and using cachePersistencePlugin, the app yields an error similar to the one above for the VS Code Azure Account extension:

Error: Persistent token caching was requested, but no persistence provider was configured. You must install the identity-cache-persistence plugin package (`npm install --save @azure/identity-cache-persistence`) and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling `useIdentityPlugin(cachePersistencePlugin)` before using `tokenCachePersistenceOptions`.
    at new MsalNode (.../node_modules/@azure/identity/src/msal/nodeFlows/nodeCommon.ts:93:13)
    at new MsalDeviceCode (.../node_modules/@azure/identity/src/msal/nodeFlows/msalDeviceCode.ts:28:5)
    at new DeviceCodeCredential (.../node_modules/@azure/identity/src/credentials/deviceCodeCredential.ts:52:21)

Summary

The version 2.0 release of @azure/identity contributes to an excellent Azure SDK developer and end-user experience. We created the Azure Identity library as a central, turnkey authentication solution for the Azure SDK. The new plugin architecture and packages enable an even smoother first-use experience. Apps can still opt in to features provided by NMC modules without creating a true dependency on them. These changes grew out of feedback from customers like you who use the Azure SDK for JavaScript to build amazing apps and services. For more information, see the README files corresponding to each of the relevant packages:

Azure SDK Blog Contributions

Thanks for reading this Azure SDK blog post. We hope you learned something new, and we welcome you to share the post. We’re open to Azure SDK blog contributions from our readers. To get started, contact us at azsdkblog@microsoft.com with your idea, and we’ll set you up as a guest blogger.

Author

Will Temple
Software Engineer II

0 comments

Discussion are closed.