Udostępnij za pomocą


Najlepsze rozwiązania dotyczące uwierzytelniania w bibliotece tożsamości platformy Azure dla języka JavaScript

Ten artykuł zawiera wskazówki ułatwiające zmaksymalizowanie wydajności i niezawodności aplikacji JavaScript i TypeScript podczas uwierzytelniania w usługach platformy Azure. Aby jak najlepiej wykorzystać bibliotekę tożsamości platformy Azure dla języka JavaScript, ważne jest, aby zrozumieć potencjalne problemy i techniki ograniczania ryzyka.

Używanie poświadczeń deterministycznych w środowiskach produkcyjnych

DefaultAzureCredential jest najbardziej przystępnym sposobem rozpoczęcia pracy z biblioteką tożsamości platformy Azure, ale ta wygoda wprowadza również pewne kompromisy. Co najważniejsze, nie można z góry zagwarantować, które konkretne poświadczenie w łańcuchu odniesie sukces i będzie używane do uwierzytelniania żądań. W środowisku produkcyjnym ta nieprzewidywalność może powodować znaczące, a czasami subtelne problemy.

Rozważmy na przykład następującą hipotetyczną sekwencję zdarzeń:

  1. Zespół ds. zabezpieczeń organizacji nakazuje wszystkim aplikacjom używanie tożsamości zarządzanej do uwierzytelniania w zasobach platformy Azure.
  2. Przez wiele miesięcy aplikacja JavaScript hostowana na maszynie wirtualnej platformy Azure pomyślnie używa DefaultAzureCredential funkcji do uwierzytelniania za pośrednictwem tożsamości zarządzanej.
  3. Nie informując zespołu wsparcia, programista instaluje Azure CLI na tej maszynie wirtualnej i uruchamia polecenie az login w celu uwierzytelnienia w platformie Azure.
  4. Ze względu na tę nową, oddzielną zmianę konfiguracji środowiska w platformie Azure, uwierzytelnianie za pośrednictwem pierwotnej tożsamości zarządzanej nieoczekiwanie zaczyna dyskretnie zawodzić.
  5. DefaultAzureCredential pomija nieudane ManagedIdentityCredential i wyszukuje następne dostępne poświadczenie, które jest AzureCliCredential.
  6. Aplikacja zaczyna używać poświadczeń Azure CLI zamiast tożsamości zarządzanej, co może prowadzić do niepowodzenia lub powodować nieoczekiwane podniesienie albo obniżenie uprawnień.

Aby zapobiec tym typom subtelnych problemów lub niepowodzeń dyskretnych w aplikacjach produkcyjnych, zastąp DefaultAzureCredential konkretną implementacją TokenCredential, taką jak ManagedIdentityCredential. Aby uzyskać dostępne poświadczenia, zobacz dokumentację biblioteki klienta tożsamości platformy Azure .

Rozważmy na przykład następującą DefaultAzureCredential konfigurację w projekcie 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
);

Zmodyfikuj powyższy kod, aby wybrać poświadczenia na podstawie środowiska, w którym działa aplikacja:

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
);

W tym przykładzie tylko ManagedIdentityCredential jest używana w środowisku produkcyjnym. Potrzeby uwierzytelniania lokalnego środowiska deweloperskiego są następnie obsługiwane przez sekwencję poświadczeń zdefiniowanych w klauzuli else.

Ponowne wykorzystanie instancji poświadczeń

Używaj ponownie instancji poświadczeń, jeśli to możliwe, aby zwiększyć odporność aplikacji i zmniejszyć liczbę żądań tokenu dostępu wysyłanych do Microsoft Entra ID. Gdy poświadczenie zostanie ponownie użyte, próbuje się pobrać token z pamięci podręcznej tokenu aplikacji zarządzanej przez zależność MSAL. Aby uzyskać więcej informacji, zobacz buforowanie tokenów w bibliotece klienta Azure Identity.

Zachowanie buforowania tokenów różni się między przeglądarką a środowiskiem Node.js. W aplikacjach Node.js tokeny są domyślnie buforowane w pamięci, co oznacza, że pamięć podręczna zostanie utracona po ponownym uruchomieniu aplikacji. W aplikacjach przeglądarki tokeny mogą być utrwalane w magazynie przeglądarki (localStorage lub sessionStorage) w zależności od przepływu uwierzytelniania i konfiguracji. Zrozumienie tych różnic jest ważne podczas implementowania strategii ponownego użycia poświadczeń dla różnych typów aplikacji.

Ważne

Aplikacja o dużym natężeniu, która nie używa ponownie poświadczeń, może napotkać odpowiedzi HTTP 429 dotyczące ograniczania przepustowości z Microsoft Entra ID, co może prowadzić do awarii aplikacji.

Zalecana strategia ponownego użycia poświadczeń różni się od struktury aplikacji.

Aby zaimplementować ponowne użycie poświadczeń w aplikacjach JavaScript, utwórz pojedyncze wystąpienie poświadczeń i użyj go ponownie we wszystkich obiektach klienta:

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
);

W aplikacjach Express.js można przechowywać poświadczenia w ustawieniach aplikacji i uzyskiwać do niej dostęp w programach obsługi tras:

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'));

Zrozum, kiedy potrzebny jest czas życia tokenu i logika buforowania

Jeśli używasz poświadczeń biblioteki tożsamości platformy Azure poza kontekstem biblioteki klienta zestawu Azure SDK, odpowiedzialność za zarządzanie okresem istnienia tokenu i zachowaniem buforowania w aplikacji staje się Twoim zadaniem.

Właściwość refreshAfterTimestampAccessToken, która zawiera wskazówkę dla konsumentów co do czasu próby odświeżenia tokenu, będzie automatycznie używana przez biblioteki klienta zestawu Azure SDK, które zależą od biblioteki platformy Azure Core w celu odświeżenia tokenu. W przypadku bezpośredniego użycia poświadczeń z biblioteki tożsamości Azure, które obsługują buforowanie tokenów, podstawowa pamięć podręczna MSAL automatycznie się odświeża po upływie refreshAfterTimestamp czasu. Ten projekt umożliwia kodowi klienta wywoływanie metody TokenCredential.getToken() za każdym razem, gdy potrzebny jest token, i delegację odświeżania do biblioteki.

Aby wywołać TokenCredential.getToken() tylko wtedy, gdy jest to konieczne, obserwuj datę refreshAfterTimestamp i proaktywnie spróbuj odświeżyć token po tym czasie. Konkretna implementacja zależy od klienta.

Zrozumienie strategii ponawiania tożsamości zarządzanej

Biblioteka tożsamości platformy Azure dla języka JavaScript umożliwia uwierzytelnianie za pomocą tożsamości zarządzanej za pomocą polecenia ManagedIdentityCredential. Sposób, w jaki używasz, ManagedIdentityCredential ma wpływ na zastosowaną strategię ponawiania prób:

  • Podczas użycia za pośrednictwem DefaultAzureCredential nie są podejmowane próby powtórnego uzyskania tokenu, gdy pierwsza próba uzyskania tokenu zakończy się niepowodzeniem lub wygaśnie limit czasu po krótkim okresie. Jest to najmniej odporna opcja, ponieważ jest zoptymalizowana pod kątem "szybkiego niepowodzenia" dla wydajnej pętli wewnętrznej programowania.
  • Inne podejście, takie jak ChainedTokenCredential lub ManagedIdentityCredential bezpośrednio:
    • Interwał czasu między ponowną próbą rozpoczyna się od 0,8 sekundy, a domyślnie jest podejmowana próba maksymalnie pięciu ponownych prób. Ta opcja jest zoptymalizowana pod kątem odporności, ale wprowadza potencjalnie niepożądane opóźnienia w pętli wewnętrznej programowania.
    • Aby zmienić dowolne z domyślnych ustawień ponawiania, użyj właściwości retryOptions w parametrze options. Na przykład spróbuj ponownie maksymalnie trzy razy z interwałem początkowym wynoszącym 0,5 sekundy:
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)
    }
  }
);

Aby uzyskać więcej informacji na temat dostosowywania zasad ponawiania dla tożsamości zarządzanej, zobacz jedną z następujących opcji, które rozszerzają jedną z opcji TokenCredentialOptions: