Поделиться через


Рекомендации по проверке подлинности с помощью библиотеки удостоверений Azure для JavaScript

В этой статье приведены рекомендации по повышению производительности и надежности приложений JavaScript и TypeScript при проверке подлинности в службах Azure. Чтобы максимально эффективно использовать библиотеку Azure Identity для JavaScript, важно понимать потенциальные проблемы и методы их устранения.

Использование детерминированных учетных данных в рабочих средах

DefaultAzureCredential — это наиболее простой способ начать работу с библиотекой идентификации Azure, но такое удобство связано с определёнными компромиссами. В частности, нельзя заранее гарантировать, какие именно учетные данные в цепочке будут успешно использованы для аутентификации запроса. В рабочей среде эта непредсказуемость может привести к значительным и иногда тонким проблемам.

Например, рассмотрим следующую гипотетическую последовательность событий:

  1. Группа безопасности организации предписывает всем приложениям использовать управляемое удостоверение для проверки подлинности в ресурсах Azure.
  2. В течение нескольких месяцев приложение JavaScript, размещенное на виртуальной машине Azure, успешно использовало DefaultAzureCredential для аутентификации с помощью управляемого удостоверения.
  3. Не сообщая группе поддержки, разработчик устанавливает Azure CLI на этой виртуальной машине и запускает команду az login для проверки подлинности в Azure.
  4. Из-за этого нового отдельного изменения конфигурации в среде Azure проверка подлинности с помощью исходной управляемой идентичности неожиданно начинает тихо давать сбой.
  5. DefaultAzureCredential пропускает недействующие ManagedIdentityCredential и ищет следующие доступные учетные данные, AzureCliCredential.
  6. Приложение начинает использовать учетные данные Azure CLI, а не управляемое удостоверение, что может привести к сбою или неожиданному повышению или сокращению привилегий.

Чтобы предотвратить возникновение таких скрытых проблем или незаметных сбоев в рабочих приложениях, замените DefaultAzureCredential на конкретную реализацию TokenCredential, например, ManagedIdentityCredential. Сведения о доступных учетных данных см. в документации по клиентской библиотеке удостоверений Azure .

Например, рассмотрим следующую DefaultAzureCredential конфигурацию в проекте Express.js:

import { DefaultAzureCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
import { BlobServiceClient } from "@azure/storage-blob";

const credential = new DefaultAzureCredential();

const secretClient = new SecretClient("https://keyVaultName.vault.azure.net", credential);
const blobServiceClient = new BlobServiceClient(
  "https://storageAccountName.blob.core.windows.net",
  credential
);

Измените предыдущий код, чтобы выбрать учетные данные в зависимости от среды, в которой выполняется приложение:

import { AzureDeveloperCliCredential, ManagedIdentityCredential, ChainedTokenCredential, 
         AzureCliCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
import { BlobServiceClient } from "@azure/storage-blob";

let credential;

// In production, use only ManagedIdentityCredential
if (process.env.NODE_ENV === 'production') {
  // For user-assigned managed identity, provide the client ID
  credential = new ManagedIdentityCredential(process.env.AZURE_CLIENT_ID);
}
// In development, use a chain of credentials appropriate for local work
else {
  credential = new ChainedTokenCredential(
    new AzureCliCredential(),
    new AzureDeveloperCliCredential()
  );
}

// Initialize Key Vault client
const secretClient = new SecretClient("https://keyVaultName.vault.azure.net", credential);

// Initialize Blob Storage client
const blobServiceClient = new BlobServiceClient(
  "https://storageAccountName.blob.core.windows.net",
  credential
);

В этом примере в рабочей среде используется только ManagedIdentityCredential. Затем требования к проверке подлинности локальной среды разработки обслуживаются последовательностью учетных данных, определенных в предложении else.

Повторное использование экземпляров учетных данных

Повторно используйте экземпляры учетных данных, если это возможно, чтобы повысить устойчивость приложения и уменьшить количество запросов на получение маркеров доступа, отправляемых в Microsoft Entra ID. При повторном использовании учетных данных предпринимается попытка получить токен из кэша токенов приложения, управляемого базовой зависимостью MSAL. Детальную информацию см. в разделе Кэширование токенов в клиентской библиотеке удостоверений Azure.

Поведение кэширования токенов отличается между браузером и средой Node.js. В приложениях Node.js маркеры кэшируются в памяти по умолчанию, что означает, что кэш теряется при перезапуске приложения. В приложениях браузера маркеры можно сохранять в хранилище браузеров (localStorage или sessionStorage) в зависимости от потока проверки подлинности и конфигурации. Понимание этих различий важно при реализации стратегий повторного использования учетных данных для различных типов приложений.

Это важно

Приложение с большим объемом данных, которое не использует учетные данные повторно, может столкнуться с ответами об ограничении HTTP 429 от Microsoft Entra ID, что может привести к сбоям в работе приложений.

Рекомендуемая стратегия повторного использования учетных данных отличается платформой приложений.

Чтобы реализовать повторное использование учетных данных в приложениях JavaScript, создайте один экземпляр учетных данных и повторно используйте его во всех клиентских объектах:

import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
import { BlobServiceClient } from "@azure/storage-blob";

// Create a single credential instance
const credential = process.env.NODE_ENV === 'production'
  ? new ManagedIdentityCredential(process.env.AZURE_CLIENT_ID)
  : new DefaultAzureCredential();

// Reuse the credential across different client objects
const secretClient = new SecretClient("https://keyVaultName.vault.azure.net", credential);
const blobServiceClient = new BlobServiceClient(
  "https://storageAccountName.blob.core.windows.net",
  credential
);

В приложениях Express.js можно хранить учетные данные в параметрах приложения и получить к нему доступ в обработчиках маршрутов:

import express from "express";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";
import { BlobServiceClient } from "@azure/storage-blob";

const app = express();

// Create a single credential instance at app startup
app.locals.credential = process.env.NODE_ENV === 'production'
  ? new ManagedIdentityCredential(process.env.AZURE_CLIENT_ID)
  : new DefaultAzureCredential();

// Reuse the credential in route handlers
app.get('/api/secrets/:secretName', async (req, res) => {
  const secretClient = new SecretClient(
    "https://keyVaultName.vault.azure.net", 
    req.app.locals.credential
  );
  
  try {
    const secret = await secretClient.getSecret(req.params.secretName);
    res.json({ name: secret.name, value: secret.value });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Add this route to the existing Express app
app.get('/api/blobs/:containerName', async (req, res) => {
  const blobServiceClient = new BlobServiceClient(
    "https://storageAccountName.blob.core.windows.net", 
    req.app.locals.credential
  );
  
  try {
    // Get reference to a container
    const containerClient = blobServiceClient.getContainerClient(req.params.containerName);
    
    // List all blobs in the container
    const blobs = [];
    for await (const blob of containerClient.listBlobsFlat()) {
      blobs.push({
        name: blob.name,
        contentType: blob.properties.contentType,
        size: blob.properties.contentLength,
        lastModified: blob.properties.lastModified
      });
    }
    
    res.json({ containerName: req.params.containerName, blobs });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

Понимание того, когда необходимо время существования токена и логика кэширования.

Если вы используете учетные данные библиотеки Azure Identity за пределами контекста клиентской библиотеки Пакета SDK Azure, вы несете ответственность за управление временем жизни токена и его кэшированием в вашем приложении.

Свойство refreshAfterTimestampAccessToken, которое предоставляет указание потребителям о том, когда можно выполнить обновление маркера, будет автоматически использоваться клиентскими библиотеками Azure SDK, которые зависят от библиотеки Azure Core для обновления маркера. Для прямого использования учетных данных библиотеки Azure Identity, поддерживающих кэширование токенов, базовый кэш MSAL автоматически обновляется заранее, когда наступает указанное время. Эта конструкция позволяет клиентскому коду вызывать TokenCredential.getToken() каждый раз, когда требуется маркер, и делегировать обновление библиотеке.

Чтобы вызывать TokenCredential.getToken() только при необходимости, обратите внимание на дату refreshAfterTimestamp и попытайтесь заранее обновить токен после этого времени. Конкретная реализация зависит от клиента.

Понять стратегию повторных попыток управляемой идентичности

Библиотека удостоверений Azure для JavaScript позволяет аутентифицироваться с помощью управляемого удостоверения ManagedIdentityCredential. То, как вы используете ManagedIdentityCredential, влияет на выбранную стратегию повторных попыток.

  • При использовании с помощью DefaultAzureCredential не предпринимаются повторные попытки, когда начальная попытка получения маркера завершается сбоем или истекает время ожидания после короткого периода. Это наименее устойчивый вариант, так как он оптимизирован для концепции "быстрого сбоя", что способствует эффективному циклу разработки.
  • Любой другой подход, например, прямое использование ChainedTokenCredential или ManagedIdentityCredential:
    • Интервал времени между повторными попытками начинается в 0,8 секунды, а по умолчанию выполняется не более пяти повторных попыток. Этот параметр оптимизирован для устойчивости, но приводит к потенциально нежелательным задержкам во внутреннем цикле разработки.
    • Чтобы изменить любой из параметров повтора по умолчанию, используйте свойство retryOptions в параметре параметров. Например, повторите попытку не более трех раз с начальным интервалом в 0,5 секунды:
import { ManagedIdentityCredential } from "@azure/identity";

const credential = new ManagedIdentityCredential(
  process.env.AZURE_CLIENT_ID, // For user-assigned managed identity
  {
    retryOptions: {
      maxRetries: 3,           // Maximum number of retry attempts
      retryDelayInMs: 500,     // Initial delay between retries (in milliseconds)
      maxRetryDelayInMs: 5000  // Maximum delay between retries (in milliseconds)
    }
  }
);

Дополнительные сведения о настройке политик повторных попыток для управляемого удостоверения см. в одном из следующих вариантов, которые расширяются от TokenCredentialOptions: