在 Azure Active Directory B2C 中使用 OAuth 2.0 隱含流程的單頁應用程式登入

許多新式應用程式都有一個單頁應用程式 (SPA) 前端,主要是以 JavaScript 撰寫。 通常會使用 React、Angular 或 Vue.js 等架構來撰寫應用程式。 主要在瀏覽器上執行的 SPA 和其他 JavaScript 應用程式,在驗證時會面臨一些額外的挑戰:

  • 這些應用程式的安全性特性與傳統的伺服器架構 Web 應用程式不同。

  • 許多授權伺服器與識別提供者不支援跨原始來源資源共用 (CORS) 要求。

  • 重新導向離開應用程式的完整網頁瀏覽器,對使用者體驗有侵入性。

支援 SPA 的建議方式是 OAuth 2.0 授權碼流程 (使用 PKCE)

有些架構 (例如 MSAL.js 1.x) 只支援隱含授與流程。 在這些情況下,Azure Active Directory B2C (Azure AD B2C) 支援 OAuth 2.0 授權隱含授與流程。 流程會在 OAuth 2.0 規格的 4.2 節中描述。 在此隱含流程中,應用程式會直接從 Azure AD B2C 授權端點接收權杖,而不需要執行任何伺服器對伺服器交換。 所有驗證邏輯和工作階段處理都是使用頁面重新導向或快顯方塊,在 JavaScript 用戶端中完成。

Azure AD B2C 會擴充標準的 OAuth 2.0 隱含流程,功能更強大,而不僅止於簡單的驗證與授權。 Azure AD B2C 導入了原則參數。 利用原則參數,您可以使用 OAuth 2.0 來為應用程式新增原則,例如註冊、登入和設定檔管理使用者流程。 在本文的範例 HTTP 要求中,我們會使用 {tenant}.onmicrosoft.com 來進行說明。 如果您有租用戶名稱,請以您的租用戶名稱取代 {tenant}。 此外,您必須已經建立使用者流程

我們使用下圖來說明隱含登入流程。 本文稍後將詳細說明每個步驟。

顯示 OpenID Connect 隱含流程的泳道樣式圖表

傳送驗證要求

當 Web 應用程式需要驗證使用者和執行使用者流程時,便會將使用者導向至 Azure AD B2C 的 /authorize 端點。 使用者會根據使用者流程採取動作。

在這項要求中,用戶端會在 scope 參數中指出所需向使用者要求的權限和所要執行的使用者流程。 為了瞭解要求的運作方式,請試著將要求貼到瀏覽器來執行。 將:

  • {tenant} 取代為您的 Azure AD B2C 租用戶名稱。

  • 90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6 取代為您在租用戶中註冊應用程式的應用程式識別碼。

  • {policy} 取代為您在租用戶中建立的原則名稱,例如 b2c_1_sign_in

GET https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize?
client_id=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6
&response_type=id_token+token
&redirect_uri=https%3A%2F%2Faadb2cplayground.azurewebsites.net%2F
&response_mode=fragment
&scope=openid%20offline_access
&state=arbitrary_data_you_can_receive_in_the_response
&nonce=12345

下表會說明 HTTP GET 要求中的參數。

參數 必要 描述
{tenant} Yes Azure AD B2C 租用戶的名稱
{policy} Yes 您想要執行的使用者流程名稱。 指定您在 Azure AD B2C 租用戶中建立的使用者流程名稱。 例如:b2c_1_sign_inb2c_1_sign_upb2c_1_edit_profile
client_id Yes Azure 入口網站指派給您應用程式的應用程式識別碼。
response_type Yes 必須包含 OpenID Connect 登入的 id_token。 它也可能包含 response type token。 如果您使用 token,您的應用程式就能立即從授權端點接收存取權杖,而不需向授權端點進行第二次要求。 如果您使用 token 回應類型,scope 參數就必須包含範圍,以指出要對哪個資源發出權杖。
redirect_uri No 應用程式的重新導向 URI,您的應用程式可在此傳送及接收驗證回應。 它必須與您在入口網站中新增至註冊應用程式的其中一個重新導向 URI 完全相符,不過必須是 URL 編碼格式。
response_mode No 指定用來將產生的權杖送回應用程式的方法。 針對隱含流程,請使用 fragment
scope Yes 以空格分隔的範圍清單。 單一範圍值表示Microsoft Entra要求的許可權兩者識別碼。 openid 範圍指示使用識別碼權杖形式的權限,以登入使用者及取得使用者相關資料。 對於 Web 應用程式, offline_access 範圍是選擇性。 它指出您的應用程式需要重新整理權杖,才能長久存取資源。
狀態 No 包含於也會隨權杖回應傳回之要求中的值。 它可以是您想要使用之任何內容的字串。 通常會使用隨機產生的唯一值來防止跨網站偽造要求攻擊。 驗證要求出現前,也會先使用此狀態為使用者在應用程式中的狀態資訊編碼,例如,使用者先前所在的網頁或正在執行的使用者流程。
nonce Yes 要求中所含的值 (由應用程式產生),它會以宣告形式包含於產生的識別碼權杖中。 然後,應用程式可以驗證此值,以減輕權杖重新執行所造成的攻擊。 此值通常是隨機的唯一字串,可用以識別要求的來源。
Prompt No 需要的使用者互動類型。 目前唯一支援的值為 login。 此參數會強制使用者在該要求上輸入認證。 單一登入不會生效。

這是流程中的互動部分。 使用者會被要求要完成原則的工作流程。 使用者可能需要輸入自己的使用者名稱和密碼、以社交身分識別登入、註冊本機帳戶,或是其他步驟數。 使用者動作取決於使用者流程的定義方式。

當使用者完成使用者流程後,Azure AD B2C 會透過 redirect_uri 將回應傳回給您的應用程式。 它會使用 response_mode 參數中指定的方法。 對於每個使用者動作情節來說,回應完全相同,與已執行的使用者流程無關。

成功的回應

使用 response_mode=fragmentresponse_type=id_token+token 的成功回應如下所示 (內含換行符號以利閱讀):

GET https://aadb2cplayground.azurewebsites.net/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&token_type=Bearer
&expires_in=3599
&scope="90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6 offline_access",
&id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&state=arbitrary_data_you_sent_earlier
參數 描述
access_token 應用程式從 Azure AD B2C 要求的存取權杖。
token_type 權杖類型值。 Azure AD B2C 唯一支援的類型是持有人。
expires_in 存取權杖的有效時間長度 (以秒為單位)。
scope 權杖有效的範圍。 您也可以使用範圍來快取權杖,以供稍後使用。
id_token 應用程式所要求的識別碼權杖。 您可以使用識別碼權杖來確認使用者的身分識別,然後開始與使用者的工作階段。 如需識別碼權杖及其內容的詳細資料,請參閱 Azure AD B2C 權杖參考
State 如果要求中包含 state 參數,則回應中應該會出現相同的值。 應用程式應該驗證要求和回應中的 state 值完全相同。

錯誤回應

錯誤回應也能傳送到重新導向 URI,以便讓應用程式能夠適當地處理它們:

GET https://aadb2cplayground.azurewebsites.net/#
error=access_denied
&error_description=the+user+canceled+the+authentication
&state=arbitrary_data_you_can_receive_in_the_response
參數 描述
error 用於分類發生錯誤類型的程式碼。
error_description 可協助您識別驗證錯誤根本原因的特定錯誤訊息。
State 如果要求中包含 state 參數,則回應中應該會出現相同的值。 應用程式應該驗證要求和回應中的 state 值完全相同。

驗證識別碼權杖

收到識別碼權杖並不足以驗證使用者。 驗證識別碼權杖的簽章,並依照應用程式的需求來確認權杖中的宣告。 Azure AD B2C 使用 JSON Web Tokens (JWT) 和公開金鑰加密編譯來簽署權杖及驗證其是否有效。

視您偏好使用的語言而定,有許多開放原始碼程式庫可用來驗證 JWT。 建議您探索可用的開放原始碼程式庫,而不是實作您自己的驗證邏輯。 您可以使用本文中的資訊,以協助了解如何適當使用這些程式庫。

Azure AD B2C 具有 OpenID Connect 中繼資料端點。 應用程式可以使用此端點,在執行階段擷取 Azure AD B2C 的相關資訊。 這項資訊包括端點、權杖內容和權杖簽署金鑰。 您的 Azure AD B2C 租用戶中的每個使用者流程都有一份 JSON 中繼資料文件。 例如,關於在 fabrikamb2c.onmicrosoft.com 租用戶中命名為 b2c_1_sign_in 的使用者流程,適用於此流程的中繼資料文件位於:

https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_sign_in/v2.0/.well-known/openid-configuration

此設定文件的屬性之一是 jwks_uri。 相同使用者流程的值會是:

https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_sign_in/discovery/v2.0/keys

若要判斷使用了哪個使用者流程來簽署識別碼權杖 (以及可從何處擷取中繼資料),您可以使用任何下列選項:

  • 使用者流程名稱包含於 id_tokenacr 宣告中。 如需如何剖析識別碼權杖中的宣告相關資訊,請參閱 Azure AD B2C 權杖參考

  • 當您簽發要求時,請在 state 參數的值中編碼使用者流程。 然後將 state 參數解碼,以判斷使用了哪個使用者流程。

當您從 OpenID Connect 中繼資料端點取得中繼資料文件之後,就可以使用 RSA-256 公開金鑰 (位於此端點) 來驗證識別碼權杖的簽章。 此端點可能隨時會列出多個金鑰,每個都由 kid 所識別。 id_token 的標頭也包含 kid 宣告。 它指出使用了這其中哪個金鑰來簽署識別碼權杖。 如需詳細資訊 (包括了解如何驗證權杖),請參閱 Azure AD B2C 權杖參考

驗證識別碼權杖的簽章之後,也需要驗證數個宣告。 例如:

  • 驗證 nonce 宣告,以防止權杖重新執行攻擊。 其值應該是您在登入要求中所指定的內容。

  • 驗證 aud 宣告,以確保已針對您的應用程式簽發識別碼權杖。 這個值就是您應用程式的應用程式識別碼。

  • 驗證 iatexp 宣告,以確保識別碼權杖沒有過期。

OpenID Connect 核心規格中會詳細說明應執行的幾項驗證。視您的案例而定,您可能也要驗證其他宣告。 一些常見的驗證包括:

  • 確保使用者或組織已為應用程式註冊。

  • 確保使用者有適當的授權與權限。

  • 確保已發生特定的驗證強度,例如使用Microsoft Entra多重要素驗證。

如需識別碼權杖中宣告的詳細資訊,請參閱 Azure AD B2C 權杖參考

當您驗證過識別碼權杖之後,便可開始關於該使用者的工作階段。 在應用程式中,使用識別碼權杖中的宣告來取得使用者的相關資訊。 這項資訊可以用於顯示、記錄、授權等等。

取得存取權杖

如果您的 Web 應用程式只需要執行使用者流程,則可以略過接下來的幾節。 下列各節中的資訊僅適用於需要對 Web API 進行已驗證呼叫的 Web 應用程式,且該 Web API 受到 Azure AD B2C 本身的保護。

既然您已將使用者登入 SPA,您可以取得存取權杖,以呼叫受Microsoft Entra識別碼保護的 Web API。 即使您已經使用 token 回應類型收到權杖,仍可使用此方法來取得其他資源的權杖,而不需重新導向使用者進行再次登入。

在一般的 Web 應用程式流程中,您會向 /token 端點提出要求。 不過,該端點不支援 CORS 要求,因此無法選擇進行 AJAX 呼叫以取得重新整理權杖。 相反地,您可以在隱藏的 HTML iframe 元素中使用隱含流程,為其他 Web API 取得新權杖。 以下是範例 (內含換行符號以利閱讀):

https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize?
client_id=90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6
&response_type=token
&redirect_uri=https%3A%2F%2Faadb2cplayground.azurewebsites.net%2F
&scope=https%3A%2F%2Fapi.contoso.com%2Ftasks.read
&response_mode=fragment
&state=arbitrary_data_you_can_receive_in_the_response
&nonce=12345
&prompt=none
參數 必要? 描述
{tenant} 必要 Azure AD B2C 租用戶的名稱
{policy} 必要 要執行的使用者流程。 指定您在 Azure AD B2C 租用戶中建立的使用者流程名稱。 例如:b2c_1_sign_inb2c_1_sign_upb2c_1_edit_profile
client_id 必要 Azure 入口網站中指派給應用程式的應用程式識別碼。
response_type 必要 必須包含 OpenID Connect 登入的 id_token 。 它也可能包含回應類型 token。 如果您在這裡使用 token,應用程式就能立即從授權端點接收存取權杖,而不需向授權端點進行第二次要求。 如果您使用 token 回應類型,scope 參數就必須包含範圍,以指出要對哪個資源發出權杖。
redirect_uri 建議 應用程式的重新導向 URI,您的應用程式可在此傳送及接收驗證回應。 它必須與您在入口網站中註冊的其中一個重新導向 URI 完全相符,不過必須是 URL 編碼格式。
scope 必要 範圍的空格分隔清單。 若要取得權杖,請包含您針對所需資源要求的所有範圍。
response_mode 建議 指定將產生之權杖送回到應用程式要使用的方法。 針對隱含流程,請使用 fragment。 您可以指定兩種其他模式 (queryform_post),但無法在隱含流程中運作。
狀態 建議 會隨權杖回應傳回之要求中所包含的值。 它可以是您想要使用之任何內容的字串。 通常會使用隨機產生的唯一值來防止跨網站偽造要求攻擊。 在驗證要求出現之前,也會使用此狀態將應用程式中使用者狀態的相關資訊編碼。 例如,使用者所在的網頁或檢視。
nonce 必要 要求中所含的值 (由應用程式產生),它會以宣告形式包含於產生的識別碼權杖中。 然後,應用程式可以驗證此值,以減輕權杖重新執行所造成的攻擊。 此值通常是隨機的唯一字串,可用以識別要求的來源。
Prompt 必要 若要重新整理並取得隱藏 iframe 中的權杖,請使用 prompt=none 以確保 iframe 不會停滯在登入頁面上,並立即返回。
login_hint 必要 若要重新整理並取得隱藏 iframe 中的權杖,請在此提示中包含使用者的使用者名稱,以區分使用者在特定時間點可能具有的多個工作階段。 您可以使用 preferred_username 宣告從先前登入擷取使用者名稱 (需要 profile 範圍才能接收 preferred_username 宣告)。
domain_hint 必要 可以是 consumersorganizations。 若要重新整理並取得隱藏 iframe 中的權杖,請在要求中包含 domain_hint 值。 從先前登入的識別碼權杖中擷取 tid 宣告,以決定所要使用的值 (需要 profile 範圍才能接收 tid 宣告)。 如果 tid 宣告值是 9188040d-6c67-4c5b-b112-36a304b66dad,請使用 domain_hint=consumers。 否則,使用 domain_hint=organizations

一旦設定 prompt=none 參數,此要求就會立即成功或失敗,並返回您的應用程式。 藉由使用 response_mode 參數中指定的方法,以重新導向 URI 將成功的回應傳送至您的應用程式。

成功的回應

使用 response_mode=fragment 的成功回應會如下列範例所示:

GET https://aadb2cplayground.azurewebsites.net/#
access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...
&state=arbitrary_data_you_sent_earlier
&token_type=Bearer
&expires_in=3599
&scope=https%3A%2F%2Fapi.contoso.com%2Ftasks.read
參數 描述
access_token 應用程式要求的權杖。
token_type 權杖類型一律為持有人。
State 如果要求中包含 state 參數,則回應中應該會出現相同的值。 應用程式應該驗證要求和回應中的 state 值完全相同。
expires_in 存取權杖的有效時間 (以秒為單位)。
scope 存取權杖有效的範圍。

錯誤回應

錯誤回應也能傳送到重新導向 URI,以便讓應用程式能夠適當地處理它們。 針對 prompt=none,預期的錯誤會如下列範例所示:

GET https://aadb2cplayground.azurewebsites.net/#
error=user_authentication_required
&error_description=the+request+could+not+be+completed+silently
參數 描述
error 可用於將發生的錯誤分類的錯誤碼字串。 您也可以使用此字串對錯誤做出反應。
error_description 可協助您識別驗證錯誤根本原因的特定錯誤訊息。

如果您在 iframe 要求中收到此錯誤,使用者必須再次以互動方式登入以擷取新的權杖。

重新整理權杖

識別碼權杖和存取權杖都會在短期內過期。 您的應用程式必須準備好定期重新整理這些權杖。 隱含流程不允許您基於安全性理由取得重新整理權杖。 若要重新整理任一類型的權杖,請在隱藏的 HTML iframe 元素中使用隱含流程。 在授權要求中包含 prompt=none 參數。 若要接收新的 id_token 值,請務必使用 response_type=id_tokenscope=openid,以及 nonce 參數。

傳送登出要求

當您想要將使用者登出應用程式時,請將使用者重新導向到 Azure AD B2C 的登出端點。 然後,您可以在應用程式中清除使用者的工作階段。 如果不將使用者重新導向,使用者可能不需要再次輸入認證就能重新通過應用程式的驗證,因為他們與 Azure AD B2C 之間仍然存在有效的單一登入工作階段。

您只要將使用者重新導向至 end_session_endpoint (列於驗證識別碼權杖中所述的相同 OpenID Connect 中繼資料文件中) 即可。 例如:

GET https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/logout?post_logout_redirect_uri=https%3A%2F%2Faadb2cplayground.azurewebsites.net%2F
參數 必要 描述
{tenant} Yes Azure AD B2C 租用戶的名稱。
{policy} Yes 您想要用來將使用者登出應用程式的使用者流程。 此使用者流程必須與應用程式用來登入使用者的使用者流程相同。
post_logout_redirect_uri No 成功登出之後,使用者應會重新導向至 URL。如果未包含 URL,Azure AD B2C 會向使用者顯示一般訊息。
狀態 No 如果要求中包含 state 參數,則回應中應該會出現相同的值。 應用程式必須驗證要求與回應中的 state 值是否相同。

注意

將使用者導向至 end_session_endpoint,會利用 Azure AD B2C 清除使用者的部分單一登入狀態。 不過,它不會將使用者登出使用者的社交身分識別提供者工作階段。 如果使用者之後在登入時選取相同的識別提供者,該使用者不必輸入自己的認證,就能讓系統重新驗證自己的身分。 舉例來說,如果使用者想要登出您的 Azure AD B2C 應用程式,不一定代表他們想要完全登出自己的 Facebook 帳戶。 不過,如果使用本機帳戶,使用者的工作階段將會正確地結束。

後續步驟

請參閱程式碼範例:在 JavaScript SPA 中使用 Azure AD B2C 登入