자동화된 통합 테스트 실행

개발자는 개발하는 앱에서 자동화된 통합 테스트를 실행하려고 합니다. 자동화된 통합 테스트에서 Microsoft ID 플랫폼(또는 Microsoft Graph와 같은 기타 보호된 API)으로 보호되는 API를 호출하는 것은 어려운 일입니다. Microsoft Entra ID에는 자동화하기 어려운 대화형 사용자 로그인 프롬프트가 필요한 경우가 많습니다. 이 문서에서는 ROPC(리소스 소유자 암호 자격 증명 부여)라고 하는 비대화형 흐름을 사용하여 테스트를 위해 사용자를 자동으로 로그인시키는 방법을 설명합니다.

자동화된 통합 테스트를 준비하려면 일부 테스트 사용자를 만들고 앱 등록을 만들고 구성하고 잠재적으로 테넌트에 일부 구성을 변경합니다. 이러한 단계 중 일부는 관리자 권한이 필요합니다. 또한 프로덕션 환경에서 ROPC 흐름을 사용하지 마세요. 관리자로서 별도의 테스트 테넌트를 만들면 자동 통합 테스트를 안전하고 효과적으로 실행할 수 있습니다.

Warning

프로덕션 환경에서는 ROPC 흐름을 사용하지 마세요. 대부분의 프로덕션 시나리오에서는 더 안전한 대체 방법을 사용하며 권장합니다. ROPC 흐름은 애플리케이션에서 매우 높은 신뢰 수준을 요구하며, 다른 인증 흐름에는 없는 위험을 전달합니다. 이 흐름은 별도의 테스트 테넌트에서 테스트 목적으로만 테스트 사용자와 함께 사용해야 합니다.

Important

  • Microsoft ID 플랫폼은 개인 계정이 아닌 Microsoft Entra 테넌트 내의 ROPC만 지원합니다. 이는 테넌트별 엔드포인트(https://login.microsoftonline.com/{TenantId_or_Name}) 또는 organizations 엔드포인트를 사용해야 함을 의미합니다.
  • Microsoft Entra 테넌트에 초대된 개인 계정은 ROPC를 사용할 수 없습니다.
  • 암호가 없는 계정은 ROPC로 로그인할 수 없습니다. 즉, SMS 로그인, FIDO 및 Authenticator 앱과 같은 기능이 해당 흐름에서 작동하지 않습니다.
  • 사용자가 MFA(Multi-Factor Authentication)를 사용하여 애플리케이션에 로그인해야 하는 경우, 사용자가 차단됩니다.
  • ROPC는 하이브리드 ID 페더레이션 시나리오(예: 온-프레미스 계정을 인증하는 데 사용되는 Microsoft Entra ID 및 AD FS(Active Directory Federation Services))에서 지원되지 않습니다. 사용자가 전체 페이지를 온-프레미스 ID 공급자로 리디렉션하는 경우 Microsoft Entra ID는 해당 ID 공급자에 대해 사용자 이름과 암호를 테스트할 수 없습니다. 그러나 통과 인증은 ROPC에서 지원됩니다.
  • 하이브리드 ID 페더레이션 시나리오의 예외는 다음과 같습니다. AllowCloudPasswordValidation이 TRUE로 설정된 홈 영역 검색 정책을 사용하면 온-프레미스 암호가 클라우드와 동기화될 때 ROPC 흐름이 페더레이션된 사용자에 대해 작동할 수 있습니다. 자세한 내용은 레거시 애플리케이션에 페더레이션된 사용자의 직접 ROPC 인증 사용을 참조하세요.

별도의 테스트 테넌트 만들기

ROPC 인증 흐름을 사용하는 것은 프로덕션 환경에서 위험하므로 별도의 테넌트를 만들어 애플리케이션을 테스트합니다. 기존 테스트 테넌트를 사용할 수 있지만 다음 단계 중 일부에는 관리자 권한이 필요하므로 테넌트의 관리자여야 합니다.

주요 자격 증명 모음 만들기 및 구성

테스트 사용자 이름과 암호를 Azure Key Vault에 비밀로 안전하게 저장하는 것이 좋습니다. 나중에 테스트를 실행하면 보안 주체의 컨텍스트에서 테스트가 실행됩니다. 보안 주체는 테스트를 로컬(예: Visual Studio 또는 Visual Studio Code)에서 실행하는 경우 Microsoft Entra 사용자이며, Azure Pipelines 또는 다른 Azure 리소스에서 테스트를 실행하는 경우 서비스 주체 또는 관리 ID입니다. 보안 주체는 테스트 실행자가 키 자격 증명 모음에서 테스트 사용자 이름과 암호를 가져올 수 있도록 읽기목록 보안 비밀 권한이 있어야 합니다. 자세한 내용은 Azure Key Vault에서 인증을 참조하세요.

  1. 아직 없는 경우 새 키 자격 증명 모음을 만듭니다.
  2. 이 문서 뒷부분의 테스트 예에서 사용되는 Vault URI 속성 값(https://<your-unique-keyvault-name>.vault.azure.net/과 유사)을 기록해 둡니다.
  3. 테스트를 실행하는 보안 주체에 대해 액세스 정책을 할당합니다. 사용자, 서비스 주체 또는 관리 ID에 키 자격 증명 모음에서 비밀 가져오기나열 권한을 부여합니다.

테스트 사용자 만들기

이 문서의 단계는 시작하는 포털에 따라 약간 다를 수 있습니다.

테스트를 위해 테넌트에 일부 테스트 사용자를 만듭니다. 테스트 사용자는 실제 사람이 아니므로 복잡한 암호를 할당하고 이러한 암호를 Azure Key Vault에 비밀로 안전하게 저장하는 것이 좋습니다.

  1. 최소한 클라우드 애플리케이션 관리자Microsoft Entra 관리 센터에 로그인합니다.
  2. ID>사용자>모든 사용자로 이동합니다.
  3. 새 사용자를 선택하고 디렉터리에 하나 이상의 테스트 사용자 계정을 만듭니다.
  4. 이 문서 뒷부분의 테스트 예에서는 단일 테스트 사용자를 사용합니다. 이전에 만든 키 자격 증명 모음에 테스트 사용자 이름과 암호를 비밀로 추가합니다. 사용자 이름을 "TestUserName"이라는 비밀로 추가하고 암호를 "TestPassword"라는 비밀로 추가합니다.

앱 등록 만들기 및 구성

테스트 중에 API를 호출할 때 클라이언트 앱 역할을 하는 애플리케이션을 등록합니다. 이는 이미 프로덕션 환경에 있는 동일한 애플리케이션이 아닙니다. 테스트용으로만 사용할 별도의 앱이 있어야 합니다.

애플리케이션 등록

앱 등록을 만듭니다. 앱 등록 빠른 시작의 단계를 따를 수 있습니다. 리디렉션 URI를 추가하거나 자격 증명을 추가할 필요가 없으므로 해당 섹션을 건너뛸 수 있습니다.

이 문서 뒷부분의 테스트 예에서 사용되는 애플리케이션(클라이언트) ID를 기록해 둡니다.

공개 클라이언트 흐름에 대해 앱 사용하도록 설정

ROPC는 공개 클라이언트 흐름이므로 공개 클라이언트 흐름에 대해 앱을 사용하도록 설정해야 합니다. Microsoft Entra 관리 센터의 앱 등록에서 인증>고급 설정>공개 클라이언트 흐름 허용으로 이동합니다. 토글을 로 설정합니다.

ROPC는 대화형 흐름이 아니므로 런타임에 이에 대한 동의 화면이 표시되지 않습니다. 토큰 획득 시 오류를 방지하기 위해 권한에 사전 동의합니다.

앱에 권한을 추가합니다. 앱에 중요하거나 높은 권한을 추가하지 마세요. 테스트 시나리오의 범위를 Microsoft Entra ID와의 통합과 관련된 기본 통합 시나리오로 범위를 지정하는 것이 좋습니다.

Microsoft Entra 관리 센터의 앱 등록에서 API 권한>권한 추가로 이동합니다. 사용할 API를 호출하는 데 필요한 권한을 추가합니다. 이 문서의 추가 테스트 예에서는 https://graph.microsoft.com/User.Readhttps://graph.microsoft.com/User.ReadBasic.All 권한을 사용합니다.

권한이 추가되면 해당 권한에 동의해야 합니다. 권한에 동의하는 방법은 테스트 앱이 앱 등록과 동일한 테넌트에 있는지 여부와 테넌트의 관리자인지 여부에 따라 다릅니다.

앱 및 앱 등록이 동일한 테넌트에 있고 관리자입니다.

앱을 등록한 동일한 테넌트에서 앱을 테스트할 계획이고 해당 테넌트의 관리자인 경우 Microsoft Entra 관리 센터에서 권한에 동의할 수 있습니다. Azure Portal의 앱 등록에서 API 권한으로 이동하여 권한 추가 단추 옆에 있는 <your_tenant_name>에 대한 관리자 동의 부여 단추를 선택한 다음 를 클릭하여 확인합니다.

앱 및 앱 등록이 다른 테넌트에 있거나 관리자가 아닙니다.

앱을 등록한 동일한 테넌트에서 앱을 테스트할 계획이 없거나 테넌트의 관리자가 아닌 경우 Microsoft Entra 관리 센터의 권한에 동의할 수 없습니다. 그러나 웹 브라우저에서 로그인 프롬프트를 트리거하여 일부 권한에 여전히 동의할 수 있습니다.

Microsoft Entra 관리 센터의 앱 등록에서 인증>플랫폼 구성>플랫폼 추가>으로 이동합니다. 리디렉션 URI https://localhost"을 추가하고 구성을 선택합니다.

관리자가 아닌 사용자가 Azure Portal을 통해 사전 동의할 수 있는 방법이 없으므로 브라우저에서 다음 요청을 보냅니다. 로그인 화면이 표시되면 이전 단계에서 만든 테스트 계정으로 로그인합니다. 메시지가 표시되는 권한에 동의합니다. 호출하고 사용하려는 사용자를 테스트하려는 각 API에 대해 이 단계를 반복해야 할 수도 있습니다.

// Line breaks for legibility only

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={your_client_ID}
&response_type=code
&redirect_uri=https://localhost
&response_mode=query
&scope={resource_you_want_to_call}/.default
&state=12345

{tenant}를 테넌트 ID로, {your_client_ID}를 애플리케이션의 클라이언트 ID로, {resource_you_want_to_call}을 식별자 URI(예: "https://graph.microsoft.com") 또는 액세스하려는 API의 앱 ID로 바꿉니다.

MFA 정책에서 테스트 앱 및 사용자 제외

테넌트에는 Microsoft에서 권장하는 대로 모든 사용자에 대해 MFA(다단계 인증)이 필요한 조건부 액세스 정책이 있을 수 있습니다. MFA는 ROPC와 함께 작동하지 않으므로 이 요구 사항에서 테스트 애플리케이션 및 테스트 사용자를 제외해야 합니다.

사용자 계정을 제외하려면:

  1. 최소한 클라우드 애플리케이션 관리자Microsoft Entra 관리 센터에 로그인합니다.
  2. 왼쪽 탐색 창에서 ID>보안 센터로 이동하여 조건부 액세스를 선택합니다.
  3. 정책에서 MFA가 필요한 조건부 액세스 정책을 선택합니다.
  4. 사용자 또는 워크로드 ID를 선택합니다.
  5. 제외 탭을 선택한 다음 사용자 및 그룹 확인란을 선택합니다.
  6. 제외된 사용자 선택에서 제외할 사용자 계정을 선택합니다.
  7. 선택 단추를 선택한 다음 저장을 선택합니다.

테스트 애플리케이션을 제외하려면:

  1. 정책에서 MFA가 필요한 조건부 액세스 정책을 선택합니다.
  2. 클라우드 앱 또는 작업을 선택합니다.
  3. 제외 탭을 선택한 다음 제외 클라우드 앱 선택을 선택합니다.
  4. 제외 클라우드 앱 선택에서 제외할 앱을 선택합니다.
  5. 선택 단추를 선택한 다음 저장을 선택합니다.

애플리케이션 테스트 작성

이제 설정이 완료되었으므로 자동화된 테스트를 작성할 수 있습니다. 다음은 다음에 대한 테스트입니다.

  1. .NET 예제 코드는 일반적인 테스트 프레임워크인 MSAL(Microsoft 인증 라이브러리)xUnit을 사용합니다.
  2. JavaScript 예제 코드는 일반적인 테스트 프레임워크인 MSAL(Microsoft 인증 라이브러리)Playwright를 사용합니다.

appsettings.json 파일 설정

이전에 만든 테스트 앱의 클라이언트 ID, 필요한 범위, 키 자격 증명 모음 URI를 테스트 프로젝트의 appsettings.json 파일에 추가합니다.

{
  "Authentication": {
    "AzureCloudInstance": "AzurePublic", //Will be different for different Azure clouds, like US Gov
    "AadAuthorityAudience": "AzureAdMultipleOrgs",
    "ClientId": <your_client_ID>
  },

  "WebAPI": {
    "Scopes": [
      //For this Microsoft Graph example.  Your value(s) will be different depending on the API you're calling
      "https://graph.microsoft.com/User.Read",
      //For this Microsoft Graph example.  Your value(s) will be different depending on the API you're calling
      "https://graph.microsoft.com/User.ReadBasic.All"
    ]
  },

  "KeyVault": {
    "KeyVaultUri": "https://<your-unique-keyvault-name>.vault.azure.net//"
  }
}

모든 테스트 클래스에서 사용할 클라이언트 설정

SecretClient()를 사용하여 Azure Key Vault에서 테스트 사용자 이름과 암호 비밀을 가져옵니다. 코드는 Key Vault가 제한되는 경우 다시 시도에 지수 백오프를 사용합니다.

DefaultAzureCredential()은 환경 변수 또는 관리 ID로 구성된 서비스 주체에서 액세스 토큰을 가져와서 Azure Key Vault로 인증합니다(코드가 관리 ID가 있는 Azure 리소스에서 실행되는 경우). 코드가 로컬에서 실행 중인 경우 DefaultAzureCredential은 로컬 사용자의 자격 증명을 사용합니다. Azure Identity 클라이언트 라이브러리 콘텐츠에서 자세히 알아봅니다.

MSAL(Microsoft 인증 라이브러리)을 사용하여 ROPC 흐름을 사용하여 인증하고 액세스 토큰을 가져옵니다. 액세스 토큰은 HTTP 요청에서 전달자 토큰으로 전달됩니다.

using Xunit;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using System.Security;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.Extensions.Configuration;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Azure.Core;
using System;

public class ClientFixture : IAsyncLifetime
{
    public HttpClient httpClient;

    public async Task InitializeAsync()
    {
        var builder = new ConfigurationBuilder().AddJsonFile("<path-to-json-file>");

        IConfigurationRoot Configuration = builder.Build();

        var PublicClientApplicationOptions = new PublicClientApplicationOptions();
        Configuration.Bind("Authentication", PublicClientApplicationOptions);
        var app = PublicClientApplicationBuilder.CreateWithApplicationOptions(PublicClientApplicationOptions)
            .Build();

        SecretClientOptions options = new SecretClientOptions()
        {
            Retry =
                {
                    Delay= TimeSpan.FromSeconds(2),
                    MaxDelay = TimeSpan.FromSeconds(16),
                    MaxRetries = 5,
                    Mode = RetryMode.Exponential
                 }
        };

        string keyVaultUri = Configuration.GetValue<string>("KeyVault:KeyVaultUri");
        var client = new SecretClient(new Uri(keyVaultUri), new DefaultAzureCredential(), options);

        KeyVaultSecret userNameSecret = client.GetSecret("TestUserName");
        KeyVaultSecret passwordSecret = client.GetSecret("TestPassword");

        string password = passwordSecret.Value;
        string username = userNameSecret.Value;
        string[] scopes = Configuration.GetSection("WebAPI:Scopes").Get<string[]>();
        SecureString securePassword = new NetworkCredential("", password).SecurePassword;

        AuthenticationResult result = null;
        httpClient = new HttpClient();

        try
        {
            result = await app.AcquireTokenByUsernamePassword(scopes, username, securePassword)
                .ExecuteAsync();
        }
        catch (MsalException) { }

        string accessToken = result.AccessToken;
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
    }

    public Task DisposeAsync() => Task.CompletedTask;
}

테스트 클래스에서 사용

다음 예는 Microsoft Graph를 호출하는 테스트입니다. 이 테스트를 고유의 애플리케이션 또는 API에서 테스트하려는 것으로 대체합니다.

public class ApiTests : IClassFixture<ClientFixture>
{
    ClientFixture clientFixture;

    public ApiTests(ClientFixture clientFixture)
    {
        this.clientFixture = clientFixture;
    }

    [Fact]
    public async Task GetRequestTest()
    {
        var testClient = clientFixture.httpClient;
        HttpResponseMessage response = await testClient.GetAsync("https://graph.microsoft.com/v1.0/me");
        var responseCode = response.StatusCode.ToString();
        Assert.Equal("OK", responseCode);
    }
}