執行自動化整合測試

身為開發人員,您想要在開發的應用程式上執行自動化整合測試。 在自動化整合測試中呼叫受 Microsoft 身分識別平台 保護的 API(或其他受保護的 API,例如 Microsoft Graph)是一項挑戰。 Microsoft Entra ID 通常需要互動式使用者登入提示,這很難自動化。 本文說明如何使用稱為 資源擁有者密碼認證授與(ROPC)的非互動式流程,自動登入用戶進行測試。

若要準備自動化整合測試,請建立一些測試使用者、建立和設定應用程式註冊,並可能對您的租用戶進行一些設定變更。 其中一些步驟需要系統管理員許可權。 此外,Microsoft 建議您 不要 在生產環境中使用 ROPC 流程。 建立身為系統管理員的個別測試租使用者 ,以便安全地有效地執行自動化整合測試。

警告

Microsoft 建議您不要在生產環境中使用 ROPC 流程。 在大部分的生產案例中,有更安全的替代方案可供使用,並建議使用。 ROPC 流程在應用程式中需要非常高的信任度,而且具有在其他驗證流程中不存在的風險。 您應該只針對個別測試租使用者中的測試目的使用此流程,且只與測試使用者搭配使用。

重要

  • Microsoft 身分識別平台 僅支援 Microsoft Entra 租使用者內的 ROPC,而不是個人帳戶。 這表示您必須使用租使用者特定的端點 (https://login.microsoftonline.com/{TenantId_or_Name}) 或 organizations 端點。
  • 受邀加入 Microsoft Entra 租用戶的個人帳戶無法使用 ROPC。
  • 沒有密碼的帳戶無法使用 ROPC 登入,這表示 SMS 登入、FIDO 和 Authenticator 應用程式等功能將無法使用該流程。
  • 如果使用者必須使用多重要素驗證 (MFA) 來登入應用程式,則會遭到封鎖。
  • 混合式身分識別同盟案例不支援 ROPC(例如,用來驗證內部部署帳戶的 Microsoft Entra ID 和 Active Directory 同盟服務 (AD FS)。 如果以整頁方式將使用者重新導向至內部部署身分識別提供者,則 Microsoft Entra ID 無法測試該身分識別提供者的使用者名稱和密碼。 不過,ROPC 支援傳遞驗證
  • 混合式身分識別同盟案例的例外狀況如下:將 AllowCloudPasswordValidation 設定為 TRUE 的主領域探索原則可讓 ROPC 流程在內部部署密碼同步至雲端時為同盟用戶運作。 如需詳細資訊,請參閱為舊版應用程式啟用同盟使用者的直接 ROPC 驗證

建立個別的測試租使用者

在生產環境中使用 ROPC 驗證流程有風險,因此 請建立個別的租用戶 來測試您的應用程式。 您可以使用現有的測試租使用者,但您必須是租使用者中的系統管理員,因為下列步驟中有一些需要系統管理員許可權。

建立及設定金鑰保存庫

建議您安全地將測試使用者名稱和密碼儲存為 Azure 金鑰保存庫 中的秘密。 當您稍後執行測試時,測試會在安全性主體的內容中執行。 如果您是在本機執行測試,則安全性主體是 Microsoft Entra 使用者(例如,在 Visual Studio 或 Visual Studio Code 中),如果您是在 Azure Pipelines 或其他 Azure 資源中執行測試,則為服務主體或受控識別。 安全性主體必須具有 讀取列出 秘密許可權,測試執行器才能從密鑰保存庫取得測試使用者名稱和密碼。 如需詳細資訊,請參閱 Azure 金鑰保存庫 中的驗證。

  1. 如果您還沒有金鑰保存庫,請建立新的金鑰保存庫
  2. 記下 本文稍後範例測試中使用的保存庫 URI 屬性值 (類似 https://<your-unique-keyvault-name>.vault.azure.net/)。
  3. 為執行測試的安全性主體指派存取原則 。 授與金鑰保存庫中的用戶、服務主體或受控識別 取得列出 秘密許可權。

建立測試使用者

提示

本文中的步驟可能會根據您從開始的入口網站稍有不同。

在您的租使用者中建立一些測試使用者以進行測試。 由於測試使用者不是實際的人類,因此建議您指派複雜的密碼,並將這些密碼安全地儲存為 Azure 金鑰保存庫 中的秘密

  1. 以至少雲端應用程式 管理員 istrator 身分登入 Microsoft Entra 系統管理中心
  2. 流覽至 [身分>識別使用者>所有使用者]。
  3. 選取 [ 新增使用者 ],然後在您的目錄中建立一或多個測試用戶帳戶。
  4. 本文稍後的範例測試會使用單一測試使用者。 將測試使用者名稱和密碼新增為您先前建立之金鑰保存庫中的 秘密。 將使用者名稱新增為名為 「TestUserName」 的秘密,並將密碼新增為名為 「TestPassword」 的秘密。

建立並設定應用程式註冊

註冊在測試期間呼叫 API 時做為用戶端應用程式的應用程式。 這不應該與您在生產環境中已有的應用程式相同。 您應該有個別的應用程式僅供測試之用。

註冊應用程式

建立應用程式註冊。 您可以遵循應用程式註冊快速入門中的步驟。 您不需要新增重新導向 URI 或新增認證,因此您可以略過這些區段。

記下 本文稍後範例測試中使用的應用程式(用戶端)標識碼

為公用用戶端流程啟用您的應用程式

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 入口網站 的應用程式註冊中,移至 [API 許可權],然後選取 [新增許可權] 按鈕旁的 [授與系統管理員同意 <your_tenant_name]> 按鈕,然後選取 [是] 以確認。

應用程式與應用程式註冊位於不同的租使用者中,或您不是系統管理員

如果您不打算在註冊應用程式的相同租用戶中測試您的應用程式,或您不是租使用者中的系統管理員,則無法同意來自 Microsoft Entra 系統管理中心的許可權。 不過,您仍然可以在網頁瀏覽器中觸發登入提示,同意某些許可權。

在 Microsoft Entra 系統管理中心的應用程式註冊中,移至 [驗證>平台>設定] [新增平臺>Web]。 新增重新導向 URI “https://localhost"然後選取 [ 設定]。

非系統管理員用戶無法透過 Azure 入口網站 預先同意,因此請在瀏覽器中傳送下列要求。 出現登入畫面提示時,請使用您在上一個步驟中建立的測試帳戶登入。 同意系統提示您的許可權。 您可能需要針對您想要呼叫的每個 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} 取代為您的租使用者標識符、{your_client_ID} 取代為應用程式的用戶端標識符,並將 {resource_you_want_to_call} 取代為標識符 URI(例如,“https://graph.microsoft.com")或您嘗試存取之 API 的應用程式識別碼。

從 MFA 原則排除測試應用程式和使用者

您的租使用者可能會有條件式存取原則, 需要所有使用者的多重要素驗證 (MFA),如 Microsoft 所建議。 MFA 無法與 ROPC 搭配運作,因此您必須免除測試應用程式和測試使用者的需求。

若要排除使用者帳戶:

  1. 以至少雲端應用程式 管理員 istrator 身分登入 Microsoft Entra 系統管理中心
  2. 流覽至左側瀏覽窗格中的 [身分>識別資訊安全中心],然後選取 [條件式存取]。
  3. 在 [ 原則] 中,選取需要 MFA 的條件式存取原則。
  4. 選取 [ 使用者或工作負載身分識別]。
  5. 選取 [ 排除] 索引標籤,然後選取 [ 使用者和群組 ] 複選框。
  6. 在 [選取排除的使用者] 中選取要排除的用戶帳戶。
  7. 選取 [ 選取 ] 按鈕,然後選取 [ 儲存]。

若要排除測試應用程式:

  1. 在 [ 原則] 中,選取需要 MFA 的條件式存取原則。
  2. 選取 [雲端應用程式或動作]
  3. 選取 [ 排除] 索引卷標,然後 選取 [排除的雲端應用程式]。
  4. 在 [選取排除的雲端應用程式] 中 ,選取您要排除的應用程式
  5. 選取 [ 選取 ] 按鈕,然後選取 [ 儲存]。

撰寫應用程式測試

現在您已設定好,您可以撰寫自動化測試。 以下是的測試:

  1. .NET 範例程序代碼使用 Microsoft 驗證連結庫 (MSAL)xUnit,這是常見的測試架構。
  2. JavaScript 範例程式代碼使用 Microsoft 驗證連結庫 (MSAL)Playwright,這是常見的測試架構。

設定 appsettings.json 檔案

將您先前建立的測試應用程式的用戶端標識碼、必要的範圍和金鑰保存庫 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 金鑰保存庫 取得測試使用者名稱和密碼秘密。 如果發生節流 金鑰保存庫,程式代碼會針對重試使用指數輪詢。

DefaultAzureCredential() 會從環境變數或受控識別所設定的服務主體取得存取令牌,以向 Azure 金鑰保存庫 進行驗證(如果程式代碼是在具有受控識別的 Azure 資源上執行)。 如果程式代碼是在本機執行, DefaultAzureCredential 請使用本機用戶的認證。 深入瞭解 Azure 身分識別客戶端連結庫 內容。

使用 Microsoft 驗證連結庫 (MSAL) 以使用 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);
    }
}