다음을 통해 공유


JavaScript용 Azure ID 라이브러리를 사용한 인증 모범 사례

이 문서에서는 Azure 서비스에 인증할 때 JavaScript 및 TypeScript 앱의 성능과 안정성을 최대화하는 데 도움이 되는 지침을 제공합니다. JavaScript용 Azure ID 라이브러리를 최대한 활용하려면 잠재적인 문제 및 완화 기술을 이해하는 것이 중요합니다.

프로덕션 환경에서 결정적 자격 증명 사용

DefaultAzureCredential Azure ID 라이브러리를 시작하는 가장 쉬운 방법이지만 이러한 편리성으로 인해 특정 장단점이 발생합니다. 특히 요청 인증에 성공하고 사용되는 체인의 특정 자격 증명은 미리 보장할 수 없습니다. 프로덕션 환경에서 이러한 예측 불가능성으로 인해 중요하고 때로는 미묘한 문제가 발생할 수 있습니다.

예를 들어 다음과 같은 가상의 이벤트 시퀀스를 고려합니다.

  1. 조직의 보안 팀은 모든 앱이 관리 ID를 사용하여 Azure 리소스에 인증하도록 의무화합니다.
  2. 몇 달 동안 Azure VM(Virtual Machine)에서 호스트되는 JavaScript 앱은 관리 ID를 통해 인증하는 데 성공적으로 사용됩니다 DefaultAzureCredential .
  3. 개발자는 지원 팀에 알리지 않고 해당 VM에 Azure CLI를 설치하고 az login 명령을 실행하여 Azure에 인증합니다.
  4. Azure 환경에서 이 새로운 별도 구성 변경으로 인해 원래 관리 ID를 통한 인증이 예기치 않게 자동으로 실패하기 시작합니다.
  5. DefaultAzureCredential 실패한 ManagedIdentityCredential 건너뛰고 사용 가능한 다음 자격 증명(AzureCliCredential)을 검색합니다.
  6. 애플리케이션은 관리 ID가 아닌 Azure CLI 자격 증명을 활용하기 시작하며, 이로 인해 실패하거나 예기치 않은 권한 상승 또는 감소가 발생할 수 있습니다.

프로덕션 앱에서 이러한 유형의 미묘한 문제나 조용한 실패를 방지하려면 DefaultAzureCredential을(를) TokenCredential와 같은 특정 ManagedIdentityCredential 구현으로 교체하세요. 사용 가능한 자격 증명은 Azure ID 클라이언트 라이브러리 설명서를 참조하세요.

예를 들어 Express.js 프로젝트에서 다음 DefaultAzureCredential 구성을 고려합니다.

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 ID 클라이언트 라이브러리토큰 캐싱을 참조하세요.

토큰 캐싱 동작은 브라우저와 Node.js 환경 간에 다릅니다. Node.js 애플리케이션에서 토큰은 기본적으로 메모리에 캐시되므로 애플리케이션이 다시 시작될 때 캐시가 손실됩니다. 브라우저 애플리케이션에서 토큰은 인증 흐름 및 구성에 따라 브라우저 스토리지(localStorage 또는 sessionStorage)에 유지될 수 있습니다. 이러한 차이점을 이해하는 것은 다양한 애플리케이션 유형에 대한 자격 증명 재사용 전략을 구현할 때 중요합니다.

중요합니다

자격 증명을 다시 사용 하지 않는 대용량 앱은 Microsoft Entra ID에서 HTTP 429 제한 응답이 발생할 수 있으며 이로 인해 앱이 중단될 수 있습니다.

권장되는 자격 증명 재사용 전략은 애플리케이션 프레임워크와 다릅니다.

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 SDK 클라이언트 라이브러리의 컨텍스트 외부에서 Azure ID 라이브러리 자격 증명을 사용하는 경우 앱에서 토큰 수명 및 캐싱 동작을 관리해야 합니다.

토큰 새로 고침을 시도할 수 있는 시기에 대한 힌트를 소비자에게 제공하는 refreshAfterTimestamp의 속성은 Azure Core 라이브러리에 의존하는 Azure SDK 클라이언트 라이브러리에서 토큰을 새로 고치는 데 자동으로 사용됩니다. 토큰 캐싱을 지원하는 Azure ID 라이브러리 자격 증명을 직접 사용할 경우, 시간이 도래할 때 refreshAfterTimestamp 기본 MSAL 캐시가 자동으로 새로 고쳐집니다. 이 디자인을 통해 클라이언트 코드는 토큰이 필요할 때마다 TokenCredential.getToken() 을 호출하고 새로 고침을 라이브러리에 위임할 수 있습니다.

필요한 경우에만 TokenCredential.getToken() 호출하려면 refreshAfterTimestamp 날짜를 확인하고 해당 시간 이후에 토큰을 미리 새로 고치려고 시도합니다. 특정 구현은 고객에게 달려 있습니다.

관리 ID 재시도 전략 이해

JavaScript용 Azure ID 라이브러리를 사용하여 관리 ID ManagedIdentityCredential를 통해 인증할 수 있습니다. 사용하는 ManagedIdentityCredential 방식은 적용된 재시도 전략에 영향을 줍니다.

  • 이를 통해 DefaultAzureCredential사용하면 초기 토큰 획득 시도가 실패하거나 짧은 기간 후에 시간이 초과될 때 재시도가 시도되지 않습니다. 효율적인 개발 내부 루프를 위해 "빠르게 실패"하도록 최적화되어 있으므로 복원력이 가장 낮은 옵션입니다.
  • ChainedTokenCredential 또는 ManagedIdentityCredential을 직접 사용하는 다른 방법:
    • 재시도 사이의 시간 간격은 0.8초에서 시작되며, 기본적으로 최대 5번의 재시도가 시도됩니다. 이 옵션은 복원력에 최적화되어 있지만 개발 내부 루프에서 원치 않는 지연이 발생할 수 있습니다.
    • 기본 다시 시도 설정을 변경하려면 옵션 매개 변수에서 retryOptions 속성을 사용합니다. 예를 들어 시작 간격이 0.5초인 최대 3회를 다시 시도합니다.
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)
    }
  }
);

관리 ID에 대한 재시도 정책을 사용자 지정하는 방법에 대한 자세한 내용은 TokenCredentialOptions에서 확장되는 다음 옵션 중 하나를 참조하세요.