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

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

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

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

问题陈述

IT 管理员和监管机构经常就以下两个问题感到左右为难:向用户提示额外的身份验证因素;实现应用程序足够的安全性和政策合规性。 它可以是影响用户工作效率的强策略或对于敏感资源不够强的策略之间进行选择。

那么,如果应用能够混合这两者,该怎么办? 对于大多数场景,运行时使用较低级别的安全性,并且提示更少。 然后,在访问更多敏感数据时有条件地加强安全要求?

常见应用场景

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

先决条件

首先,应使用用于身份验证和授权的 OpenID Connect/ 协议将应用与 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 应用程序的所有用户必须成功完成双因素身份验证(2FA),以验证上下文 ID C1。
  • 登录到此 Web 应用程序的所有用户都必须成功完成 2FA,并且还必须从身份验证上下文 ID C3 的已定义的 IP 地址范围访问该应用。

注意

条件访问身份验证上下文值与应用程序分开进行声明和维护。 不建议应用程序过分依赖身份验证上下文 ID。 IT 管理员通常会创建条件访问策略,因为他们更好地了解可用的资源。 同样,如果在多个租户中使用该应用程序,则使用中的身份验证上下文 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. (可选)声明客户端功能。 客户端功能可帮助资源提供程序(RP)(如 Web API)检测客户端应用程序是否了解声明质询,然后可以相应地自定义其响应。 如果并非所有 API 客户端都能处理声明质询,并且某些较旧的 API 客户端仍期望不同的响应,则此功能可能很有用。 有关详细信息,请参阅客户端功能部分。

注意事项和建议

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

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

代码示例

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

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

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

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

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

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

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

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

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

注意

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

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

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

按间隔显示的登录频率

当所有当前身份验证因素的即时身份验证都在登录频率间隔内时,条件访问会将“按间隔显示的登录频率”视为满足机会性 ACRS 评估。 如果任一身份验证因素已过时,则按时间间隔的登录频率要求将无法满足,并且也不会在令牌中适时颁发 ACRS。

云应用安全 (CAS)

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

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

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

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

Flow 请求 ACRS 应用策略 满足控制要求 将 ACRS 添加到声明
Ariel 请求访问令牌 “c1” 无 “c1”满足。 “c2”和“c3”不满足 “c1”(已请求)
Ariel 请求访问令牌 “c2” 策略 B 被策略 B 阻止 无
Ariel 请求访问令牌 无 无 “c1”满足。 “c2”和“c3”不满足 “c1”(通过策略 A 适时添加)
Jay 在未进行 MFA 的情况下请求访问令牌 “c1” 策略 A No 无
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”全部适时添加

后续步骤