Authenticate JavaScript apps to Azure services during local development using service principals

When you create cloud applications, developers need to debug and test applications on their local workstation. When an application is run on a developer's workstation during local development, it still must authenticate to any Azure services used by the app. This article covers how to set up dedicated application service principal objects to be used during local development.

A diagram showing how a JavaScript app during local development uses the developer's credentials to connect to Azure by obtaining those credentials locally installed development tools.

Dedicated application service principals for local development allow you to follow the principle of least privilege during app development. Since permissions are scoped to exactly what is needed for the app during development, app code is prevented from accidentally accessing an Azure resource intended for use by a different app. This method also prevents bugs from occurring when the app is moved to production because the app was overprivileged in the dev environment.

An application service principal is set up for the app when the app is registered in Azure. When registering apps for local development, it's recommended to:

  • Create separate app registrations for each developer working on the app. This method creates separate application service principals for each developer to use during local development and avoid the need for developers to share credentials for a single application service principal.
  • Create separate app registrations per app. This scopes the app's permissions to only what is needed by the app.

During local development, environment variables are set with the application service principal's identity. The Azure SDK for JavaScript reads these environment variables and uses this information to authenticate the app to the Azure resources it needs.

1 - Register the application in Azure

Application service principal objects are created with an app registration in Azure. You can create service principals using either the Azure portal or Azure CLI.

Sign in to the Azure portal and follow these steps.

Instructions Screenshot
In the Azure portal:
  1. Enter app registrations in the search bar at the top of the Azure portal.
  2. Select the item labeled App registrations under the Services heading on the menu that appears below the search bar.
A screenshot showing how to use the top search bar in the Azure portal to find and navigate to the App registrations page.
On the App registrations page, select + New registration. A screenshot showing the location of the New registration button in the App registrations page.
On the Register an application page, fill out the form as follows.
  1. Name → Enter a name for the app registration in Azure. It is recommended this name include the app name, the user the app registration is for, and an identifier like 'dev' to indicate this app registration is for use in local development.
  2. Supported account typesAccounts in this organizational directory only.
Select Register to register your app and create the application service principal.
A screenshot showing how to fill out the Register an application page by giving the app a name and specifying supported account types as accounts in this organizational directory only.
On the App registration page for your app:
  1. Application (client) ID → This is the app ID the app will use to access Azure during local development. Copy this value to a temporary location in a text editor as you'll need it in a future step.
  2. Directory (tenant) id → This value will also be needed by your app when it authenticates to Azure. Copy this value to a temporary location in a text editor it will also be needed it in a future step.
  3. Client credentials → You must set the client credentials for the app before your app can authenticate to Azure and use Azure services. Select Add a certificate or secret to add credentials for your app.
A screenshot after the app registration has been completed with the location of the application ID, tenant ID.
On the Certificates & secrets page, select + New client secret. A screenshot showing the location of the link to use to create a new client secret on the certificates and secrets page.
The Add a client secret dialog will pop out from the right-hand side of the page. In this dialog:
  1. Description → Enter a value of Current.
  2. Expires → Select a value of 24 months.
Select Add to add the secret.
A screenshot showing the page where a new client secret is added for the application service principal create by the app registration process.
On the Certificates & secrets page, you'll be shown the value of the client secret.

Copy this value to a temporary location in a text editor as you'll need it in a future step.

IMPORTANT: This is the only time you will see this value. Once you leave or refresh this page, you won't be able to see this value again. You may add more client secrets without invalidating this client secret, but you won't see this value again.
A screenshot showing the page with the generated client secret.

2 - Create a Microsoft Entra security group for local development

Since there typically multiple developers who work on an application, it's recommended to create a Microsoft Entra group to encapsulate the roles (permissions) the app needs in local development rather than assigning the roles to individual service principal objects. This offers the following advantages.

  • Every developer is assured to have the same roles assigned since roles are assigned at the group level.
  • If a new role is needed for the app, it only needs to be added to the Microsoft Entra group for the app.
  • If a new developer joins the team, a new application service principal is created for the developer and added to the group, assuring the developer has the right permissions to work on the app.
Instructions Screenshot
Navigate to the Microsoft Entra ID page in the Azure portal by typing Microsoft Entra ID into the search box at the top of the page and then selecting Microsoft Entra ID from under services. A screenshot showing how to use the top search bar in the Azure portal to search for and navigate to the Microsoft Entra ID page.
On the Microsoft Entra ID page, select Groups from the left-hand menu. A screenshot showing the location of the Groups menu item in the left-hand menu of the Microsoft Entra ID Default Directory page.
On the All groups page, select New group. A screenshot showing the location of the New Group button in the All groups page.
On the New Group page:
  1. Group typeSecurity
  2. Group name → A name for the security group, typically created from the application name. It is also helpful to include a string like local-dev in the name of the group to indicate the purpose of the group.
  3. Group description → A description of the purpose of the group.
  4. Select the No members selected link under Members to add members to the group.
A screenshot showing how to create a new Microsoft Entra group for the application.
On the Add members dialog box:
  1. Use the search box to filter the list of principal names in the list.
  2. Select the application service principals for local development for this app. As objects are selected, they will be greyed out and move to the Selected items list at the bottom of the dialog.
  3. When finished, select the Select button.
A screenshot of the Add members dialog box showing how to select application service principals to be included in the group.
Back on the New group page, select Create to create the group.

The group will be created and you will be taken back to the All groups page. It may take up to 30 seconds for the group to appear and you may need to refresh the page due to caching in the Azure portal.
A screenshot of the New Group page showing how to complete the process by selecting the Create button.

3 - Assign roles to the application

Next, you need to determine what roles (permissions) your app needs on what resources and assign those roles to your app. In this example, the roles are assigned to the Microsoft Entra group created in step 2. Roles can be assigned a role at a resource, resource group, or subscription scope. This example shows how to assign roles at the resource group scope since most applications group all their Azure resources into a single resource group.

Instructions Screenshot
Locate the resource group for your application by searching for the resource group name using the search box at the top of the Azure portal.

Navigate to your resource group by selecting the resource group name under the Resource Groups heading in the dialog box.
A screenshot showing how to use the top search box in the Azure portal to locate and navigate to the resource group you want to assign roles (permissions) to.
On the page for the resource group, select Access control (IAM) from the left-hand menu. A screenshot of the resource group page showing the location of the Access control (IAM) menu item.
On the Access control (IAM) page:
  1. Select the Role assignments tab.
  2. Select + Add from the top menu and then Add role assignment from the resulting drop-down menu.
A screenshot showing how to navigate to the role assignments tab and the location of the button used to add role assignments to a resource group.
The Add role assignment page lists all of the roles that can be assigned for the resource group.
  1. Use the search box to filter the list to a more manageable size. This example shows how to filter for Storage Blob roles.
  2. Select the role that you want to assign.
    Select Next to go to the next screen.
A screenshot showing how to filter and select role assignments to be added to the resource group.
The next Add role assignment page allows you to specify what user to assign the role to.
  1. Select User, group, or service principal under Assign access to.
  2. Select + Select members under Members
A dialog box opens on the right-hand side of the Azure portal.
A screenshot showing the radio button to select to assign a role to a Microsoft Entra group and the link used to select the group to assign the role to.
In the Select members dialog:
  1. The Select text box can be used to filter the list of users and groups in your subscription. If needed, type the first few characters of the local development Microsoft Entra group you created for the app.
  2. Select the local development Microsoft Entra group associated with your application.
Select Select at the bottom of the dialog to continue.
A screenshot showing how to filter for and select the Microsoft Entra group for the application in the Select members dialog box.
The Microsoft Entra group shows as selected on the Add role assignment screen.

Select Review + assign to go to the final page and then Review + assign again to complete the process.
A screenshot showing the completed Add role assignment page and the location of the Review + assign button used to complete the process.

4 - Set local development environment variables

The DefaultAzureCredential object looks for the service principal information in a set of environment variables at runtime. Since most developers work on multiple applications, it's recommended to use a package like dotenv to access environment from a .env file stored in the application's directory during development. This scopes the environment variables used to authenticate the application to Azure such that they can only be used by this application.

The .env file is never checked into source control since it contains the application secret key for Azure. The standard .gitignore file for JavaScript automatically excludes the .env file from check-in.

To use the dotenv package, first install the package in your application.

npm install dotenv

Then, create a .env file in your application root directory. Set the environment variable values with values obtained from the app registration process as follows:

  • AZURE_CLIENT_ID → The app ID value.
  • AZURE_TENANT_ID → The tenant ID value.
  • AZURE_CLIENT_SECRET → The password/credential generated for the app.
AZURE_CLIENT_ID=00000000-0000-0000-0000-000000000000
AZURE_TENANT_ID=11111111-1111-1111-1111-111111111111
AZURE_CLIENT_SECRET=abcdefghijklmnopqrstuvwxyz

Finally, in the startup code for your application, use the dotenv library to read the environment variables from the .env file on startup.

import 'dotenv/config'

5 - Implement DefaultAzureCredential in your application

To authenticate Azure SDK client objects to Azure, your application should use the DefaultAzureCredential class from the @azure/identity package. In this scenario, DefaultAzureCredential detects the environment variables AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_CLIENT_SECRET are set and read those variables to get the application service principal information to connect to Azure with.

Start by adding the @azure/identity package to your application.

npm install @azure/identity

Next, for any JavaScript code that creates an Azure SDK client object in your app, you'll want to:

  1. Import the DefaultAzureCredential class from the @azure/identity module.
  2. Create a DefaultAzureCredential object.
  3. Pass the DefaultAzureCredential object to the Azure SDK client object constructor.

An example of this is shown in the following code segment.

// Azure authentication dependency
import { DefaultAzureCredential } from '@azure/identity';

// Azure resource management dependency
import { SubscriptionClient } from "@azure/arm-subscriptions";

// Acquire credential
const tokenCredential = new DefaultAzureCredential();

async function listSubscriptions() {
  try {

    // use credential to authenticate with Azure SDKs
    const client = new SubscriptionClient(tokenCredential);

    // get details of each subscription
    for await (const item of client.subscriptions.list()) {
      const subscriptionDetails = await client.subscriptions.get(
        item.subscriptionId
      );
      /* 
        Each item looks like:
      
        {
          id: '/subscriptions/123456',
          subscriptionId: '123456',
          displayName: 'YOUR-SUBSCRIPTION-NAME',
          state: 'Enabled',
          subscriptionPolicies: {
            locationPlacementId: 'Internal_2014-09-01',
            quotaId: 'Internal_2014-09-01',
            spendingLimit: 'Off'
          },
          authorizationSource: 'RoleBased'
        },
    */
      console.log(subscriptionDetails);
    }
  } catch (err) {
    console.error(JSON.stringify(err));
  }
}

listSubscriptions()
  .then(() => {
    console.log("done");
  })
  .catch((ex) => {
    console.log(ex);
  });

DefaultAzureCredential will automatically detect the authentication mechanism configured for the app and obtain the necessary tokens to authenticate the app to Azure. If an application makes use of more than one SDK client, the same credential object can be used with each SDK client object.