使用 OAuth 2.0、Azure Active Directory B2C 和 Azure API 管理來保護 SPA 後端

此案例說明如何設定 Azure API 管理實例來保護 API。 我們將使用 Azure AD B2C SPA (Auth Code + PKCE) 流程來取得權杖,並使用 API 管理來保護 Azure Functions 後端使用 EasyAuth。

目標

我們將會在 Azure Functions 和 Azure AD B2C 的簡化案例中,瞭解如何使用 API 管理。 您將建立呼叫 API 的 JavaScript (JS) 應用程式,該應用程式會以 Azure AD B2C 登入使用者。 然後,您將使用 API 管理的驗證-jwt、CORS 和速率限制(依金鑰原則功能)來保護後端 API。

針對深層防禦,我們接著會使用 EasyAuth 在後端 API 內再次驗證權杖,並確保 API 管理是唯一可呼叫 Azure Functions 後端的服務。

您將學到什麼

  • 在 Azure Active Directory B2C 中設定單一頁面應用程式和後端 API
  • 建立 Azure Functions 後端 API
  • 將 Azure Functions API 匯入至 Azure API 管理
  • 保護 Azure API 管理中的 API
  • 透過 Microsoft 身分識別平臺程式庫 (MSAL.js 呼叫 Azure Active Directory B2C 授權端點)
  • 儲存 HTML/香草 JS 單一頁面應用程式,並從 Azure Blob 儲存體端點提供服務

必要條件

若要依照本文中的步驟進行,您必須有:

  • Azure (StorageV2) 一般用途 V2 儲存體帳戶,以裝載前端 JS 單一頁面應用程式。
  • (任何層級的 Azure API 管理實例都可運作,包括「耗用量」,但此階層中不提供適用于完整案例的特定功能 (依金鑰限制和專用虛擬 IP) ,這些限制會在下列適當) 的文章中加以呼叫。
  • 空白的 Azure 函數應用程式, (在取用方案) 上執行3.1 版 .NET Core 執行時間,以裝載呼叫的 API
  • 連結至訂用帳戶的 Azure AD B2C 租使用者。

雖然在實務上,您會在生產工作負載中使用相同區域中的資源,但在此操作說明文章中,部署的區域並不重要。

概觀

以下是在此程式完成後,使用中的元件以及兩者之間的流程的說明。 使用中的元件和流程

以下是步驟的快速總覽:

  1. 使用範圍和授與 API 存取權來建立 Azure AD B2C 呼叫 (前端、API 管理) 和 API 應用程式

  2. 建立註冊和登入原則,以允許使用者使用 Azure AD B2C 登入

  3. 使用新的 Azure AD B2C 用戶端識別碼和金鑰來設定 API 管理,以在開發人員主控台中啟用 OAuth2 使用者授權

  4. 建立函數 API

  5. 設定函數 API,以使用新的 Azure AD B2C 用戶端識別碼和金鑰來啟用 EasyAuth,並鎖定至 APIM VIP

  6. 在 API 管理中建立 API 定義

  7. 設定 API 管理 API 設定的 Oauth2

  8. 設定 CORS 原則,並新增 驗證 jwt 原則,以驗證每個連入要求的 OAuth 權杖

  9. 建立呼叫應用程式以使用 API

  10. 上傳 JS SPA 範例

  11. 使用新的 Azure AD B2C 用戶端識別碼和金鑰來設定範例 JS 用戶端應用程式

  12. 測試用戶端應用程式

    提示

    我們將在逐步解說這份檔時,取得相當多的資訊和金鑰,您可能會發現,有一個開啟文字編輯器的方便,可以暫時儲存下列的設定專案。

    B2C 後端用戶端識別碼:
    B2C 後端用戶端秘密金鑰:
    B2C 後端 API 範圍 URI:
    B2C 前端用戶端識別碼:
    B2C 使用者流程端點 URI:
    B2C 知名的 OPENID 端點:
    B2C 原則名稱: Frontendapp_signupandsignin 函數 URL:
    APIM API 基底 URL:儲存體主要端點 URL:

設定後端應用程式

在入口網站中開啟 Azure AD B2C 分頁,然後執行下列步驟。

  1. 選取 [ 應用程式註冊 ] 索引標籤

  2. 按一下 [新增註冊] 按鈕。

  3. 從 [重新導向 URI 選擇] 方塊中選擇 [Web]。

  4. 現在設定顯示名稱,選擇唯一的名稱,以及與所建立之服務相關的內容。 在此範例中,我們將使用「後端應用程式」名稱。

  5. 使用回復 url 的預留位置,例如 ' https://jwt.ms ' (Microsoft 擁有的權杖解碼網站) ,我們稍後會更新這些 url。

  6. 確定您已選取 [使用使用者流程驗證使用者的任何識別提供者或組織目錄 (中的帳戶) ] 選項

  7. 針對此範例,請取消核取 [授與系統管理員同意] 方塊,因為我們目前不需要 offline_access 許可權。

  8. 按一下 [註冊]。

  9. 記錄後端應用程式用戶端識別碼,以供稍後使用 (在 [應用程式 (用戶端) 識別碼] ) 下顯示。

  10. 選取 [管理]) (的 [ 憑證和密碼 ] 索引標籤,然後按一下 [新增用戶端密碼] 以產生驗證金鑰 (接受預設設定,然後按一下 [新增] ) 。

  11. 按一下 [新增] 時,將 [值] 底下的金鑰 (複製 ) 安全的位置,以供稍後用作「後端用戶端密碼」-請注意,此對話方塊是您唯一必須複製此金鑰的機會。

  12. 現在,在 [管理) ] 底下選取 [ 公開 API ] 索引標籤 (。

  13. 系統會提示您設定 AppID URI,請選取並記錄預設值。

  14. 為您的函式 API 建立範圍 "Hello" 並為其命名,您可以對所有 enterable 選項使用 ' Hello ' 片語,並記錄填入的完整範圍值 URI,然後按一下 [新增領域]。

  15. 選取入口網站左上角的 [Azure AD B2C] 階層連結,返回 Azure AD B2C 分頁的根目錄。

    注意

    Azure AD B2C 範圍是您 API 中的有效許可權,讓其他應用程式可以透過應用程式的 API 存取分頁要求存取,而您只是為您所呼叫的 API 建立應用程式許可權。

設定前端應用程式

  1. 選取 [ 應用程式註冊 ] 索引標籤
  2. 按一下 [新增註冊] 按鈕。
  3. 從 [重新導向 URI 選擇] 方塊中選擇 [單一頁面應用程式 (SPA) ]。
  4. 現在設定顯示名稱和 AppID URI,選擇唯一且與前端應用程式相關的內容,此應用程式將會使用此 AAD B2C 應用程式註冊。 在此範例中,您可以使用「前端應用程式」
  5. 根據第一個應用程式註冊,將支援的帳戶類型選項保留為預設 (使用使用者流程驗證使用者)
  6. 使用回復 url 的預留位置,例如 ' https://jwt.ms ' (Microsoft 擁有的權杖解碼網站) ,我們稍後會更新這些 url。
  7. 讓 [授與管理員同意] 方塊保持核取
  8. 按一下 [註冊]。
  9. 記錄前端應用程式用戶端識別碼,以供稍後使用 (在 [應用程式 (用戶端) 識別碼] ) 下顯示。
  10. 切換至 [ API 許可權 ] 索引標籤。
  11. 依序按一下 [新增許可權]、[我的 Api]、[後端應用程式]、[許可權],選取您在上一節中建立的範圍,然後按一下 [新增許可權],以授與後端應用程式的存取權
  12. 按一下 [授與 {tenant} 的管理員同意],然後從快顯對話方塊中按一下 [是]。 此快顯視窗會同意「前端應用程式」,以使用稍早建立的「後端應用程式」中定義的「hello」許可權。
  13. 擁有權限現在應該會顯示為應用程式的 [狀態] 資料行下的綠色刻度

建立「註冊並登入」使用者流程

  1. 選取 Azure AD B2C 階層連結,返回 B2C blade 的根目錄。

  2. 切換至 [原則]) 索引標籤下的 [消費者流程] (。

  3. 按一下 [新增使用者流程]

  4. 選擇 [註冊並登入] 使用者流程類型,然後選取 [建議],然後選取 [建立]

  5. 提供原則的名稱,並加以記錄以供稍後使用。 在此範例中,您可以使用 "Frontendapp_signupandsignin",請注意,這會在前面加上 "B2C_1_" 以使 "B2C_1_Frontendapp_signupandsignin"

  6. 在 [識別提供者] 和 [本機帳戶] 下,視您的 B2C 租使用者的設定而定,選取 [電子郵件註冊] (或 [使用者識別碼註冊]) 然後按一下 [確定]。 這是因為我們將會註冊本機 B2C 帳戶,而不會延後到另一個識別提供者 (例如社交識別提供者,) 使用使用者現有的社交媒體帳戶。

  7. 將 MFA 和條件式存取設定保留為預設值。

  8. 在 [使用者屬性和宣告] 底下,按一下 [顯示更多 ...]然後選擇您要讓使用者輸入並在權杖中傳回的宣告選項。 請檢查至少「顯示名稱」和「電子郵件地址」以進行收集,並使用「顯示名稱」和「電子郵件地址」來傳回 (特別注意您收集 emailaddress、單數和要求傳回電子郵件地址的事實、多個) ,然後按一下 [確定],然後按一下 [建立]。

  9. 按一下您在清單中建立的使用者流程,然後按一下 [執行使用者流程] 按鈕。

  10. 此動作會開啟 [執行使用者流程] 分頁、選取前端應用程式、複製使用者流程端點,並加以儲存以供稍後使用。

  11. 將連結複製並儲存在頂端,並記錄為「已知的 openid 設定端點」以供稍後使用。

    注意

    B2C 原則可讓您公開 Azure AD B2C 的登入端點,以便能夠以不同的方式來捕獲不同的資料元件和登入使用者。

    在此情況下,我們設定了註冊或登入流程 (原則) 。 這也公開了知名的設定端點,在這兩種情況下,我們所建立的原則都是在 URL 中以 "p =" 查詢字串參數來識別。

    完成這項操作之後,您現在可以使用企業對消費者身分識別平臺,將使用者登入多個應用程式。

建立函數 API

  1. 切換回您在 Azure 入口網站中的標準 Azure AD 租使用者,讓我們可以在您的訂用帳戶中再次設定專案。

  2. 移至 Azure 入口網站的函式應用程式分頁,開啟您的空白函數應用程式,然後按一下 [函式],再按一下 [新增]。

  3. 在出現的飛出視窗中,選擇 [在入口網站中開發],然後在 [選取範本] 下選擇 [HTTP 觸發程式],然後在 [範本詳細資料] 底下,將 [函式] 命名為 [hello],然後選取 [新增]。

  4. 切換至程式碼 + 測試分頁,然後將下列範例程式碼複製並貼到出現的 現有程式碼上

  5. 選取 [儲存]。

    
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    
    public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
    {
       log.LogInformation("C# HTTP trigger function processed a request.");
    
       return (ActionResult)new OkObjectResult($"Hello World, time and date are {DateTime.Now.ToString()}");
    }
    
    

    提示

    您剛剛貼上的 c # 腳本函式程式碼只會將一行記錄到函式記錄檔中,並傳回文字 "Hello World",其中包含) 日期和時間 (的一些動態資料。

  6. 從左側分頁中選取 [整合],然後按一下 [觸發程式] 方塊內的 [HTTP (需求]) 連結。

  7. 從 [選取的 HTTP 方法] 下拉式清單中,取消核取 [HTTP POST] 方法,只保留 [已選取],然後按一下 [儲存]。

  8. 切換回 [程式碼 + 測試] 索引標籤,按一下 [取得函式 URL],然後複製顯示的 URL 並加以儲存,以供稍後之用。

    注意

    您剛才建立的系結只會告知函式對您剛才複製的 URL 進行匿名 HTTP GET 要求的回應, (https://yourfunctionappname.azurewebsites.net/api/hello?code=secretkey) 。 現在我們有一個可擴充的無伺服器 HTTPs API,它能夠傳回非常簡單的裝載。

    您現在可以使用您剛剛複製和儲存的 URL 版本,從網頁瀏覽器測試呼叫此 API。 您也可以移除 URL 的查詢字串參數 "? code = secretkey" 部分,然後再測試一次,以證明 Azure Functions 會傳回401錯誤。

設定和保護函數 API

  1. 函數應用程式中有兩個額外的區域必須設定 (授權和網路限制) 。

  2. 首先,讓我們設定驗證/授權,然後透過階層連結回到函式應用程式的根分頁。

  3. 接下來,選取 [設定] ) 下的 [驗證/授權] (。

  4. 開啟 App Service Authentication 功能。

  5. 將 [要求未經驗證時所要採取的動作] 下拉式清單設定為 [使用 Azure Active Directory 登入]。

  6. 在 [驗證提供者] 下,選擇 [Azure Active Directory]。

  7. 從管理模式參數選擇 [Advanced (Advanced)]。

  8. 將後端應用程式的 [應用程式] 用戶端識別碼 (從 Azure AD B2C) 貼到 [用戶端識別碼] 方塊中

  9. 將知名的 open id 設定端點從 [註冊並登入] 原則貼到 [簽發者 URL] 方塊中, (我們稍早將此設定記錄) 。

  10. 按一下 [顯示秘密],並將後端應用程式的用戶端密碼貼到適當的方塊中。

  11. 選取 [確定],這會將您帶回 [識別提供者選取] 頁面/畫面。

  12. 在 [advanced settings] 下啟用 權杖存放區 , (預設) 。

  13. 按一下 blade) 左上角的 [儲存] (。

    重要

    現在已部署您的函式 API,如果未提供正確的 JWT 作為授權,則應該擲回401回應:持有人標頭,且應在呈現有效要求時傳回資料。
    您可以設定 [使用 Azure AD 登入] 選項來處理未經驗證的要求,以在 EasyAuth 中新增額外的深度防禦安全性。 請注意,這會變更後端函式應用程式和前端 SPA 之間的未經授權要求行為,因為 EasyAuth 會發出302重新導向至 AAD,而不是401未授權的回應,我們會在稍後使用 API 管理來修正此問題。

    我們仍未套用任何 IP 安全性,如果您有有效的金鑰和 OAuth2 權杖,任何人都可以從任何地方呼叫此項-理想情況下,我們會想要強制所有要求都通過 API 管理。

    如果您使用的是 APIM 耗用量層,則不會有 專用的 AZURE API 管理虛擬 IP 可允許使用函式存取限制清單。 在 Azure API 管理標準 SKU 和更新版本中, VIP 是單一租使用者和資源的存留期。 針對 Azure API 管理耗用量層,您可以在上面複製的 URI 部分中,透過共用密碼函式金鑰來鎖定 API 呼叫。 此外,對於使用層-下列步驟12-17 不適用。

  14. 關閉 [驗證/授權] 分頁

  15. 開啟 入口網站的 [API 管理] 分頁,然後開啟 您的實例

  16. 記錄顯示在 [總覽] 索引標籤上的私用 VIP。

  17. 返回 入口網站的 Azure Functions 分頁,然後再次開啟 您的實例

  18. 選取 [網路],然後選取 [設定存取限制]

  19. 按一下 [新增規則],然後在步驟3中輸入以 xx. xx. xx/32 格式複製的 VIP。

  20. 如果您想要繼續與函式入口網站互動,並執行下列選用步驟,您也應該在此新增您自己的公用 IP 位址或 CIDR 範圍。

  21. 一旦清單中有允許專案,Azure 就會新增隱含拒絕規則來封鎖所有其他位址。

您必須將 CIDR 格式的位址區塊新增至 [IP 限制] 面板。 當您需要新增單一位址(例如 API 管理 VIP)時,您必須以 xx. xx. xx/32 格式來新增。

注意

現在您的函式 API 不應從任何地方(透過 API 管理或您的位址)進行呼叫。

  1. 開啟 [ API 管理] 分頁,然後開啟 您的實例

  2. 在 [Api) ] 底下選取 [Api] 分頁 (。

  3. 從 [新增 API] 窗格中,選擇 [函數應用程式],然後從快顯視窗頂端選取 [完整]。

  4. 按一下 [流覽],選擇您要在其中裝載 API 的函式應用程式,然後按一下 [選取]。 接下來,再按一下 [選取]。

  5. 針對 api 管理的內部使用提供 API 的名稱和描述,並將其新增至「無限制」產品。

  6. 複製並記錄 API 的 [基底 URL],然後按一下 [建立]。

  7. 按一下 [設定] 索引標籤,然後在 [訂用帳戶] 下關閉 [需要訂用帳戶] 核取方塊,因為我們會在此案例中使用 Oauth JWT 權杖來速率限制。 請注意,如果您使用的是使用量層,則在生產環境中仍然需要這項功能。

    提示

    如果使用 APIM 的取用層,無限制的產品將不會以現成的形式提供。 請改為流覽至「Api」下的「產品」,然後按 [新增]。
    輸入「無限制」作為產品名稱和描述,然後選取您剛剛從畫面左下角的「+」 Api 標注新增的 API。 選取 [已發佈] 核取方塊。 將其餘部分保留為預設值。 最後,按 [建立] 按鈕。 這會建立「無限制」的產品,並將其指派給您的 API。 您稍後可以自訂新的產品。

設定和捕獲正確的儲存體端點設定

  1. 在 Azure 入口網站中開啟 [儲存體帳戶] 分頁

  2. 選取您所建立的帳戶,然後從 [設定] 區段中選取 [靜態網站] 分頁 (如果您沒有看到 [靜態網站] 選項,請確認您已建立 V2 帳戶) 。

  3. 將靜態網站裝載功能設定為 [已啟用],並將 [索引檔案名稱] 設定為 [index.html],然後按一下 [儲存]。

  4. 請記下 [主要端點] 的內容,因為此位置是前端網站的裝載位置。

    提示

    您可以使用 Azure Blob 儲存體 + CDN 重寫,或 Azure App Service 來裝載 SPA-但是 Blob 儲存體的靜態網站裝載功能可提供預設的容器,以從 Azure 儲存體提供靜態 web 內容/html/js/css,並且會為我們推斷零工作的預設頁面。

設定 CORS驗證 jwt 原則

無論使用的 APIM 層為何,都應該遵循下列各節。 儲存體帳戶 URL 來自您將在本文最上方的必要條件中提供的儲存體帳戶。

  1. 切換至入口網站的 [API 管理] 分頁,並開啟您的實例。

  2. 選取 [Api],然後選取 [所有 Api]。

  3. 在 [輸入處理] 底下,按一下程式碼視圖按鈕 "</>",以顯示原則編輯器。

  4. 編輯輸入區段並貼上下列 xml,使其看起來像下面這樣。

  5. 取代原則中的下列參數

  6. {PrimaryStorageEndpoint} (您在上一節中複製的「主要儲存體端點」) ,{b2cpolicy-知名-openid} (您先前複製) 和 {後端 API-應用程式-識別碼} 中的「已知的 openid 設定端點」, (後端 api 的 B2C 應用程式/用戶端識別碼) 儲存了先前儲存的正確值。

  7. 如果您使用 API 管理的取用層,則應同時移除每個速率限制原則,因為當使用 Azure API 管理的取用層時,無法使用此原則。

    <inbound>
       <cors allow-credentials="true">
             <allowed-origins>
                 <origin>{PrimaryStorageEndpoint}</origin>
             </allowed-origins>
             <allowed-methods preflight-result-max-age="120">
                 <method>GET</method>
             </allowed-methods>
             <allowed-headers>
                 <header>*</header>
             </allowed-headers>
             <expose-headers>
                 <header>*</header>
             </expose-headers>
         </cors>
       <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-expiration-time="true" require-signed-tokens="true" clock-skew="300">
          <openid-config url="{b2cpolicy-well-known-openid}" />
          <required-claims>
             <claim name="aud">
                <value>{backend-api-application-client-id}</value>
             </claim>
          </required-claims>
       </validate-jwt>
       <rate-limit-by-key calls="300" renewal-period="120" counter-key="@(context.Request.IpAddress)" />
       <rate-limit-by-key calls="15" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Subject)" />
    </inbound>
    

    注意

    Azure API 管理現在可以回應來自您的 JavaScript SPA 應用程式的跨原始要求,它會在將要求轉送至函式 API 之前,對所傳遞的 JWT 驗證權杖執行節流、速率限制和預先驗證。

    恭喜,您現在已 Azure AD B2C、API 管理和 Azure Functions 合作,以發佈、保護和取用 API!

    提示

    如果您使用 API 管理耗用量層,而不是根據 JWT 主體或連入 IP 位址的速率限制 (則目前不支援「取用」層) 的「依金鑰原則的呼叫率」限制,您可以在 這裡查看。
    因為此範例是 JavaScript 單一頁面應用程式,我們只會使用 API 管理金鑰進行速率限制和帳單通話。 實際的授權和驗證是由 Azure AD B2C 處理,並封裝在 JWT 中,它會透過 API 管理,然後由後端 Azure 函數進行驗證兩次。

將 JavaScript SPA 範例上傳至靜態儲存體

  1. 仍在 [儲存體帳戶] 分頁中,從 [Blob 服務] 區段中選取 [容器] 分頁,然後按一下右側窗格中顯示 $web 容器。

  2. 將下列程式碼儲存至本機電腦上的檔案做為 index.html,然後將檔案 index.html 上傳至 $web 容器。

     <!doctype html>
     <html lang="en">
     <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
          <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.11.1/js/msal-browser.min.js"></script>
     </head>
     <body>
          <div class="container-fluid">
              <div class="row">
                  <div class="col-md-12">
                     <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
                         <div class="container-fluid">
                             <a class="navbar-brand" href="#">Azure Active Directory B2C with Azure API Management</a>
                             <div class="navbar-nav">
                                 <button class="btn btn-success" id="signinbtn"  onClick="login()">Sign In</a>
                             </div>
                         </div>
                     </nav>
                  </div>
              </div>
              <div class="row">
                  <div class="col-md-12">
                      <div class="card" >
                         <div id="cardheader" class="card-header">
                             <div class="card-text"id="message">Please sign in to continue</div>
                         </div>
                         <div class="card-body">
                             <button class="btn btn-warning" id="callapibtn" onClick="getAPIData()">Call API</a>
                             <div id="progress" class="spinner-border" role="status">
                                 <span class="visually-hidden">Loading...</span>
                             </div>
                         </div>
                      </div>
                  </div>
              </div>
          </div>
          <script lang="javascript">
                 // Just change the values in this config object ONLY.
                 var config = {
                     msal: {
                         auth: {
                             clientId: "{CLIENTID}", // This is the client ID of your FRONTEND application that you registered with the SPA type in AAD B2C
                             authority:  "{YOURAUTHORITYB2C}", // Formatted as https://{b2ctenantname}.b2clogin.com/tfp/{b2ctenantguid or full tenant name including onmicrosoft.com}/{signuporinpolicyname}
                             redirectUri: "{StoragePrimaryEndpoint}", // The storage hosting address of the SPA, a web-enabled v2 storage account - recorded earlier as the Primary Endpoint.
                             knownAuthorities: ["{B2CTENANTDOMAIN}"] // {b2ctenantname}.b2clogin.com
                         },
                         cache: {
                             cacheLocation: "sessionStorage",
                             storeAuthStateInCookie: false 
                         }
                     },
                     api: {
                         scopes: ["{BACKENDAPISCOPE}"], // The scope that we request for the API from B2C, this should be the backend API scope, with the full URI.
                         backend: "{APIBASEURL}/hello" // The location that we will call for the backend api, this should be hosted in API Management, suffixed with the name of the API operation (in the sample this is '/hello').
                     }
                 }
                 document.getElementById("callapibtn").hidden = true;
                 document.getElementById("progress").hidden = true;
                 const myMSALObj = new msal.PublicClientApplication(config.msal);
                 myMSALObj.handleRedirectPromise().then((tokenResponse) => {
                     if(tokenResponse !== null){
                         console.log(tokenResponse.account);
                         document.getElementById("message").innerHTML = "Welcome, " + tokenResponse.account.name;
                         document.getElementById("signinbtn").hidden = true;
                         document.getElementById("callapibtn").hidden = false;
                     }}).catch((error) => {console.log("Error Signing in:" + error);
                 });
                 function login() {
                     try {
                         myMSALObj.loginRedirect({scopes: config.api.scopes});
                     } catch (err) {console.log(err);}
                 }
                 function getAPIData() {
                     document.getElementById("progress").hidden = false; 
                     document.getElementById("message").innerHTML = "Calling backend ... "
                     document.getElementById("cardheader").classList.remove('bg-success','bg-warning','bg-danger');
                     myMSALObj.acquireTokenSilent({scopes: config.api.scopes, account: getAccount()}).then(tokenResponse => {
                         const headers = new Headers();
                         headers.append("Authorization", `Bearer ${tokenResponse.accessToken}`);
                         fetch(config.api.backend, {method: "GET", headers: headers})
                             .then(async (response)  => {
                                 if (!response.ok)
                                 {
                                     document.getElementById("message").innerHTML = "Error: " + response.status + " " + JSON.parse(await response.text()).message;
                                     document.getElementById("cardheader").classList.add('bg-warning');
                                 }
                                 else
                                 {
                                     document.getElementById("cardheader").classList.add('bg-success');
                                     document.getElementById("message").innerHTML = await response.text();
                                 }
                                 }).catch(async (error) => {
                                     document.getElementById("cardheader").classList.add('bg-danger');
                                     document.getElementById("message").innerHTML = "Error: " + error;
                                 });
                     }).catch(error => {console.log("Error Acquiring Token Silently: " + error);
                         return myMSALObj.acquireTokenRedirect({scopes: config.api.scopes, forceRefresh: false})
                     });
                     document.getElementById("progress").hidden = true;
              }
             function getAccount() {
                 var accounts = myMSALObj.getAllAccounts();
                 if (!accounts || accounts.length === 0) {
                     return null;
                 } else {
                     return accounts[0];
                 }
             }
         </script>
      </body>
     </html>
    
  3. 流覽至您稍早在上一節中儲存的靜態網站主要端點。

    注意

    恭喜,您剛剛部署了 JavaScript 單一頁面應用程式,以 Azure 儲存體靜態內容裝載。
    由於我們尚未使用您的 Azure AD B2C 詳細資料來設定 JS 應用程式–如果您開啟該頁面,該頁面將無法運作。

設定 Azure AD B2C 的 JavaScript SPA

  1. 現在我們知道所有專案是什麼:我們可以使用適當的 API 管理 API 位址和正確的 Azure AD B2C 應用程式/用戶端識別碼來設定 SPA。
  2. 返回至 Azure 入口網站 storage blade
  3. 在 [設定] 下選取 [容器] ()
  4. 從清單中選取 [$web] 容器
  5. 從清單中選取 index.html blob
  6. 按一下 [編輯]
  7. 更新 msal config 區段中的驗證值,以符合您稍早在 B2C 中註冊的 前端 應用程式。 使用程式碼批註來取得設定值外觀的提示。 授權 單位值的格式必須為:-HTTPs://{b2ctenantname}. >b2clogin.com .com/tfp/{b2ctenantname}. onmicrosoft .com}/{signupandsigninpolicyname},如果您已使用我們的範例名稱,而您的 b2c 租使用者稱為「contoso」,則您會預期授權單位是 ' https://contoso.b2clogin.com/tfp/contoso.onmicrosoft.com}/Frontendapp_signupandsignin '。
  8. 將 api 值設定為符合您稍早記錄的 API 基底 Url (後端位址,並為 後端應用程式) 記錄 ' >b2cscopes ' 值。
  9. 按一下 [Save] (儲存)。

設定 Azure AD B2C 前端應用程式的重新導向 Uri

  1. 開啟 Azure AD B2C 分頁,然後流覽至 JavaScript 前端應用程式的應用程式註冊。

  2. 按一下 [重新導向 Uri],並刪除先前輸入的預留位置 ' https://jwt.ms '。

  3. 為主要 (儲存體) 端點新增新的 URI (減去結尾的正斜線) 。

    注意

    這項設定會導致前端應用程式的用戶端使用來自 Azure AD B2C 的適當宣告來接收存取權杖。
    SPA 將可在對後端 API 呼叫的 HTTPs 標頭中,將其新增為持有人權杖。

    API 管理會預先驗證權杖、對端點發出的速率限制呼叫(依 Azure 識別碼所發出的 JWT 的主體) (使用者) 和呼叫端的 IP 位址 (視 API 管理的服務層而定,請參閱上述) ,然後再將要求傳遞至接收的 Azure Function API,以新增函數安全性金鑰。
    SPA 將會在瀏覽器中呈現回應。

    恭喜,您已設定 Azure AD B2C、Azure API 管理、Azure Functions Azure App Service 授權,以完美的協調方式運作!

現在我們有一個簡單的應用程式,其中包含簡單的安全 API,讓我們來進行測試。

測試用戶端應用程式

  1. 開啟您稍早建立的儲存體帳戶中記下的範例應用程式 URL。
  2. 按一下右上角的 [登入],這點會顯示您的 Azure AD B2C 註冊或登入設定檔。
  3. 應用程式應該會使用您的 B2C 設定檔名稱來歡迎您。
  4. 現在按一下 [呼叫 API],頁面應該會以從您的安全 API 傳回的值進行更新。
  5. 如果您 重複 按一下 [呼叫 API] 按鈕,且您是在開發人員層或 API 管理的上方執行,您應該注意到您的解決方案將會開始對 api 進行速率限制,且應在應用程式中使用適當的訊息報告這項功能。

我們已經完成了

您可以調整和編輯上述步驟,以允許許多不同的 Azure AD B2C 使用 API 管理。

下一步