条件访问身份验证上下文开发人员指南

条件访问是零信任控制平面,可让你有针对性地使用策略来控制对所有应用(旧应用或新应用、专用应用或公共应用、本地应用或多云应用)的访问。 使用条件访问身份验证上下文,可以在这些应用中应用不同的策略。

条件访问身份验证上下文(身份验证上下文)允许将具体策略应用于敏感数据和操作,而不仅仅是在应用级别。 可以优化零信任策略来实现最低权限的访问,同时最大限度地减少用户摩擦,让用户更有生产力,让资源更加安全。 现在,由你的公司开发的、使用 OpenId Connect 进行身份验证的应用程序通过此功能来保护敏感资源(例如高价值的交易)或查看员工个人数据。

若要从应用程序和服务内触发升级身份验证,请使用 Microsoft Entra 条件访问引擎的身份验证上下文功能。 现在,开发人员可以有选择地要求实现更强身份验证,例如来自最终用户在其应用程序中的 MFA。 此功能有助于开发人员为其应用程序的大多部分构建更流畅的用户体验,但对更安全的操作和数据的访问仍滞后于更强身份验证控制。

问题陈述

在过于频繁地提示用户采取额外的身份验证措施,与在包含敏感数据和操作的部件的应用程序和服务中实现足够高的安全性与策略遵从性之间,IT 管理员和调控员往往很难做出取舍。 可以做出的选择是采用一个强策略,但这会影响用户在访问大部分数据和操作时的工作效率;或者采用一个强度不足以保护敏感资源的策略。

那么应用是否能够实现这二者的结合,即在大多数用户和操作中,它们能够以相对较低的安全性和较少的提示次数运行,但当用户访问更敏感的部分时,又有条件地提高安全要求呢?

常见应用场景

例如,尽管用户可以使用多重身份验证登录到 SharePoint,但在 SharePoint 中访问包含敏感文档的网站集可能需要使用合规的设备,并且只能从受信任的 IP 范围访问。

先决条件

首先,应使用用于身份验证和授权的 OpenID Connect/ OAuth 2.0 协议将应用与 Microsoft 标识平台集成。 建议使用 Microsoft 标识平台身份验证库在 Microsoft Entra ID 中集成和保护应用程序。 Microsoft 标识平台文档是一个很好的学习资源,其中介绍了如何将应用与 Microsoft 标识平台集成。 条件访问身份验证上下文功能支持构建在行业标准 OpenID Connect 协议提供的协议扩展基础之上。 开发人员将条件访问身份验证上下文引用值与声明请求参数一起用于为应用提供触发和满足策略的方法。

第二基于风险的条件访问需要 Microsoft Entra ID P1 许可。 在 Microsoft Entra 定价页上可以找到有关许可的详细信息。

第三,此功能目前仅适用于可将用户登录的应用程序。 不支持自身进行身份验证的应用程序。 请参考身份验证流和应用程序方案指南了解 Microsoft 标识平台中支持的身份验证应用类型和流。

集成步骤

使用支持的身份验证协议集成应用程序并将其注册到具有可用条件访问功能的 Microsoft Entra 租户后,可以开始将此功能集成到可将用户登录的应用程序中。

首先,声明身份验证上下文并使其在你的租户中可用。 有关详细信息,请参阅配置身份验证上下文

值 C1-C99 可用作租户中的身份验证上下文 ID。 身份验证上下文的示例:

  • C1 – 需要强身份验证
  • C2 - 需要符合条件的设备
  • C3 - 需要受信任的位置

创建或修改条件访问策略,以便使用条件访问身份验证上下文。 示例策略可以是:

  • 登录此 Web 应用程序的所有用户应该都已成功完成身份验证上下文 ID C1 的 2FA。
  • 所有登录此 Web 应用程序的用户应该都已成功完成 2FA,并且还可以从身份验证上下文 ID C3 的某个 IP 地址范围访问 Web 应用。

注意

条件访问身份验证上下文值与应用程序分开进行声明和维护。 不建议应用程序过分依赖身份验证上下文 ID。 条件访问策略通常由 IT 管理员制定,因为他们对可用于应用策略的资源有更深入的了解。 例如,对于 Microsoft Entra 租户,IT 管理员知道该租户中有多少用户有条件使用 2FA 进行 MFA,从而确保将需要 2FA 的条件访问策略的范围限定为这些有条件的用户。 同样,如果在多个租户中使用该应用程序,则使用中的身份验证上下文 ID 可能会不同,并且在某些情况下根本不可用。

第二:对于计划使用条件访问身份验证上下文的应用程序,建议其开发人员首先为应用程序管理员或 IT 管理员提供一种方法,将潜在敏感操作映射到身份验证上下文 ID。 步骤大致如下:

  1. 标识代码中的操作,代码可用于根据身份验证上下文 ID 进行映射。
  2. 在应用的管理门户(或等效功能)中构建一个屏幕,IT 管理员可以使用该屏幕将敏感操作映射到可用的身份验证上下文 ID。
  3. 有关此方法的示例,请参阅代码示例使用条件访问身份验证上下文执行升级身份验证

这些步骤是你需要在代码库中执行的改变。 这些步骤大致包括:

  • 查询 MS Graph,列出所有可用的身份验证上下文
  • 允许 IT 管理员选择敏感/高特权操作,并使用 CA 策略根据可用的身份验证上下文分配这些操作。
  • 将此映射信息保存在数据库/本地存储中。

创建身份验证上下文的设置流

第三:你的应用程序,在此示例中,我们假设它是一个 Web API,然后需要根据保存的映射评估调用,并相应地针对其客户端应用提出声明质询。 若要准备此操作,需要执行以下步骤:

  1. 在受身份验证上下文操作保护的敏感环境中,根据前面保存的身份验证上下文 ID 映射评估 acrs 声明中的值,并引发声明质询,如以下代码片段所示

  2. 下图显示了用户、客户端应用和 Web API 之间的交互。

    用户、网络应用程序、应用程序接口和 Microsoft Entra ID 交互示意图

    以下代码片段来自代码示例使用条件访问身份验证上下文进行逐步身份验证。 API 中的第一个方法 CheckForRequiredAuthContext()

    • 检查所调用的应用程序操作是否需要进行逐步身份验证。 为此,它会检查其数据库中是否保存了此方法的映射
    • 如果此操作确实需要提升的身份验证上下文,它会检查 acrs 声明中现有匹配的身份验证上下文 ID。
    • 如果找不到匹配的身份验证上下文 ID,则它会提出声明质询
    public void CheckForRequiredAuthContext(string method)
    {
        string authType = _commonDBContext.AuthContext.FirstOrDefault(x => x.Operation == method
                    && x.TenantId == _configuration["AzureAD:TenantId"])?.AuthContextId;
    
        if (!string.IsNullOrEmpty(authType))
        {
            HttpContext context = this.HttpContext;
            string authenticationContextClassReferencesClaim = "acrs";
    
            if (context == null || context.User == null || context.User.Claims == null
                || !context.User.Claims.Any())
            {
                throw new ArgumentNullException("No Usercontext is available to pick claims from");
            }
    
            Claim acrsClaim = context.User.FindAll(authenticationContextClassReferencesClaim).FirstOrDefault(x
                => x.Value == authType);
    
            if (acrsClaim == null || acrsClaim.Value != authType)
            {
                if (IsClientCapableofClaimsChallenge(context))
                {
                    string clientId = _configuration.GetSection("AzureAd").GetSection("ClientId").Value;
                    var base64str = Convert.ToBase64String(Encoding.UTF8.GetBytes("{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"" + authType + "\"}}}"));
    
                    context.Response.Headers.Append("WWW-Authenticate", $"Bearer realm=\"\", authorization_uri=\"https://login.partner.microsoftonline.cn/common/oauth2/authorize\", client_id=\"" + clientId + "\", error=\"insufficient_claims\", claims=\"" + base64str + "\", cc_type=\"authcontext\"");
                    context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                    string message = string.Format(CultureInfo.InvariantCulture, "The presented access tokens had insufficient claims. Please request for claims requested in the WWW-Authentication header and try again.");
                    context.Response.WriteAsync(message);
                    context.Response.CompleteAsync();
                    throw new UnauthorizedAccessException(message);
                }
                else
                {
                    throw new UnauthorizedAccessException("The caller does not meet the authentication  bar to carry our this operation. The service cannot allow this operation");
                }
            }
        }
    }
    

    注意

    Microsoft 标识平台中的声明质询一文介绍了声明质询的格式。

  3. 在客户端应用程序中,截获声明质询并将用户重定向回到 Microsoft Entra ID 以进一步评估策略。 以下代码片段来自代码示例使用条件访问身份验证上下文进行逐步身份验证

    internal static string ExtractHeaderValues(WebApiMsalUiRequiredException response)
    {
        if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized && response.Headers.WwwAuthenticate.Any())
        {
            AuthenticationHeaderValue bearer = response.Headers.WwwAuthenticate.First(v => v.Scheme == "Bearer");
            IEnumerable<string> parameters = bearer.Parameter.Split(',').Select(v => v.Trim()).ToList();
            var errorValue = GetParameterValue(parameters, "error");
    
            try
            {
                // read the header and checks if it contains error with insufficient_claims value.
                if (null != errorValue && "insufficient_claims" == errorValue)
                {
                    var claimChallengeParameter = GetParameterValue(parameters, "claims");
                    if (null != claimChallengeParameter)
                    {
                        var claimChallenge = ConvertBase64String(claimChallengeParameter);
    
                        return claimChallenge;
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        return null;
    }
    

    处理 Web API 调用中的异常,如果提供了声明质询,则将用户重定向回到 Microsoft Entra ID 以做进一步处理。

    try
    {
        // Call the API
        await _todoListService.AddAsync(todo);
    }
    catch (WebApiMsalUiRequiredException hex)
    {
        // Challenges the user if exception is thrown from Web API.
        try
        {
            var claimChallenge =ExtractHeaderValues(hex);
            _consentHandler.ChallengeUser(new string[] { "https://microsoftgraph.chinacloudapi.cn/user.read" }, claimChallenge);
    
            return new EmptyResult();
        }
        catch (Exception ex)
        {
            _consentHandler.HandleException(ex);
        }
    
        Console.WriteLine(hex.Message);
    }
    return RedirectToAction("Index");
    
  4. (可选)声明客户端功能。 客户端功能有助于 Web API 等资源提供程序 (RP) 检测调用方客户端应用程序是否理解声明质询,然后可以相应地自定义其响应。 如果并非所有 API 客户端都能处理声明质询,并且某些较旧的 API 客户端仍期望不同的响应,则此功能可能很有用。 有关详细信息,请参阅客户端功能部分。

注意事项和建议

不要在应用中对身份验证上下文值进行硬编码。 应用应使用 MS Graph 调用读取和应用身份验证上下文。 这种做法对于多租户应用程序至关重要。 身份验证上下文值因 Microsoft Entra 租户而异,在 Microsoft Entra ID 免费版中不可用。 若要详细了解应用应如何在代码中查询、设置和使用身份验证上下文,请参阅代码示例使用条件访问身份验证上下文进行逐步身份验证,了解应用应如何在代码中查询、设置和使用身份验证上下文。

如果应用本身将成为条件访问策略的目标,请不要在该应用中使用身份验证上下文。 当应用程序的某些部分要求用户满足更高的身份验证标准时,此功能就很有用。

代码示例

条件访问中的身份验证上下文 [ACR] 预期行为

请求中的显式身份验证上下文满意度

客户端可以通过请求正文中的声明使用身份验证上下文 (ACRS) 显式请求令牌。 如果请求了 ACRS,条件访问将允许使用请求的 ACRS 颁发令牌(如果所有质询都已完成)。

当身份验证上下文不受租户中的条件访问保护时的预期行为

如果分配给 ACRS 值的所有条件访问策略都已满足,条件访问可以在令牌的声明中发出 ACRS。 如果未将条件访问策略分配给 ACRS 值,则仍可能会发出声明,因为已满足所有策略要求。

显式请求 ACRS 时的预期行为的摘要表

请求 ACRS 应用策略 满足控制要求 将 ACRS 添加到声明
No
No
未使用 ACRS 配置策略

通过机会性评估得出的隐式身份验证上下文满意度

资源提供程序可以选择加入可选的“acrs”声明。 条件访问尝试适时地将 ACRS 添加到令牌声明,以避免往返获取 Microsoft Entra ID 的新令牌。 在该评估中,条件访问将检查是否已满足保护身份验证上下文质询的策略,如果满足,则将 ACRS 添加到令牌声明中。

注意

每个令牌类型都需要单独选择(ID 令牌、访问令牌)。

如果资源提供程序未选择加入可选的“acrs”声明,则获取令牌中的 ACRS 的唯一方法是在令牌请求中对其进行显式请求。 它不享受机会性评估所带来的好处,因此,每当令牌声明中缺少所需的 ACRS 时,资源提供程序都会质询客户端,以在声明中获取包含它的新令牌。

隐式 ACRS 机会性评估的身份验证上下文和会话控件的预期行为

按间隔显示的登录频率

当所有当前身份验证因素的即时身份验证都在登录频率间隔内时,条件访问会将“按间隔显示的登录频率”视为满足机会性 ACRS 评估。 如果第一个即时身份验证因素已过时,或者如果存在第二个因素 (MFA),并且其即时身份验证已过时,则按间隔显示的登录频率不满足评估,并且不会在令牌中适时地发出 ACRS。

云应用安全 (CAS)

在请求期间建立 CAS 会话时,条件访问会将 CAS 会话控制视为满足机会性 ACRS 评估。 例如,当条件访问策略在请求传入时应用并强制执行 CAS 会话,此外还有也要求进行 CAS 会话的条件访问策略,因为 CAS 会话会强制执行,所以将满足机会性评估的 CAS 会话控制要求。

当租户包含保护身份验证上下文的条件访问策略时的预期行为

下表将显示通过机会性评估将 ACRS 添加到令牌声明的所有极端情况。

策略 A:在请求“c1”acrs 时,要求所有用户(不包括用户“Ariel”)进行 MFA。 策略 B:请求“c2”或“c3”acrs 时阻止所有用户(不包括用户“Jay”)。

流向 请求 ACRS 应用策略 满足控制要求 将 ACRS 添加到声明
Ariel 请求访问令牌 “c1” “c1”满足。 “c2”和“c3”不满足 “c1”(已请求)
Ariel 请求访问令牌 “c2” 策略 B 被策略 B 阻止
Ariel 请求访问令牌 “c1”满足。 “c2”和“c3”不满足 “c1”(通过策略 A 适时添加)
Jay 在未进行 MFA 的情况下请求访问令牌 “c1” 策略 A
Jay 在进行 MFA 的情况下请求访问令牌 “c1” 策略 A “c1”(已请求),“c2”(通过策略 B 适时添加),“c3”(通过策略 B 适时添加)
Jay 在未进行 MFA 的情况下请求访问令牌 “c2” “c2”和“c3”满足。 “c1”不满足 “c2”(已请求),“c3”(通过策略 B 适时添加)
Jay 在进行 MFA 的情况下请求访问令牌 “c2” “c1”、“c2”和“c3”满足 “c1”(策略 A 的最佳效果)、“c2”(已请求)、“c3”(通过策略 B 适时添加)
Jay 在进行 MFA 的情况下请求访问令牌 “c1”、“c2”和“c3”满足 “c1”、“c2”、“c3”全部适时添加
Jay 在未进行 MFA 的情况下请求访问令牌 “c2”和“c3”满足。 “c1”不满足 “c2”、“c3”全部适时添加

后续步骤