首页 > 解决方案 > Azure AD 多租户应用程序 - 如何实现令牌验证?

问题描述

我在 Azure 广告中注册了一个多租户Web 应用程序/API,并连接到一个 API 应用程序。API 应用程序具有 Active Directory 身份验证设置。目前只有一个其他租户需要访问 api。https://sts.windows.net/<third party tenant>/我确保只有他们可以通过输入Issuer URL来获得访问权限。我的问题是:我将如何让第二个(或更多)租户访问 api?我无法在颁发者 URL中添加租户 ID,所以我有点不知所措

谢谢

标签: azureoauthoauth-2.0azure-active-directoryazure-web-app-service

解决方案


您当前使用的方法仅适用于单租户场景,即通过设置 IssuerURL 自动验证租户仅适用于单租户场景。

在多租户应用程序的情况下,应用程序负责存储和验证所有可能的颁发者。这是设计使然,Microsoft 关于此主题的确切指导可在此处获得:

在 Azure AD 中使用基于声明的身份:颁发者验证

对于单租户应用程序,您只需检查颁发者是否是您自己的租户。事实上,OIDC 中间件默认情况下会自动执行此操作。在多租户应用程序中,您需要允许多个颁发者,对应于不同的租户。这是一个通用的使用方法:

  • 在 OIDC 中间件选项中,将 ValidateIssuer 设置为 false。这将关闭自动检查。
  • 当租户注册时,将租户和颁发者存储在您的用户数据库中。
  • 每当用户登录时,在数据库中查找颁发者。如果没有找到颁发者,则表示租户尚未注册。您可以将它们重定向到注册页面。
  • 您还可以将某些租户列入黑名单;例如,对于未支付订阅费用的客户。

因此,对于基于 .NET 的 Web 应用程序,您的启动类中的代码将更改为类似这样。请注意新的 TokenValidationParameters { ValidateIssuer = false }

使用 Azure AD 和 OpenID Connect 进行身份验证

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions {
    ClientId = configOptions.AzureAd.ClientId,
    ClientSecret = configOptions.AzureAd.ClientSecret, // for code flow
    Authority = Constants.AuthEndpointPrefix,
    ResponseType = OpenIdConnectResponseType.CodeIdToken,
    PostLogoutRedirectUri = configOptions.AzureAd.PostLogoutRedirectUri,
    SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
    TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false },
    Events = new SurveyAuthenticationEvents(configOptions.AzureAd, loggerFactory),
});

禁用 Validate issuer 后,您需要自己处理验证。这是一个示例,其中包含有关如何自己进行此验证的一些指导

更新您的代码以处理多个颁发者值

在让呼叫通过之前,您至少需要根据您自己的有效租户 ID 列表检查捕获 Azure AD 租户 ID 的“tid”声明。

当单个租户应用程序验证令牌时,它会根据元数据文档中的签名密钥检查令牌的签名,并确保令牌中的颁发者值与在元数据文档中找到的值匹配。

由于 /common 端点不对应于租户并且不是颁发者,因此当您检查 /common 元数据中的颁发者值时,它具有模板化 URL 而不是实际值:

https://sts.windows.net/ {tenantid}/

因此,多租户应用程序无法仅通过将元数据中的颁发者值与令牌中的颁发者值进行匹配来验证令牌。多租户应用程序需要逻辑来根据颁发者值的租户 ID 部分来决定哪些颁发者值有效,哪些无效。

例如,如果多租户应用程序仅允许从已注册其服务的特定租户登录,则它必须检查令牌中的颁发者值或 tid 声明值,以确保该租户在他们的列表中订户。如果多租户应用程序只处理个人并且不根据租户做出任何访问决策,那么它可以完全忽略颁发者的价值。

(编辑)有关验证令牌的更多信息 我试图从这里的评论中回答您的问题。

  1. 这是执行手动验证 JWT 令牌任务的示例代码。 在 Web API 中手动验证 JWT 访问令牌

在此处输入图像描述

有用的摘录。。

验证声明 当应用程序在用户登录时收到访问令牌时,它还应该针对访问令牌中的声明执行一些检查。这些验证包括但不限于:

观众声明,以验证 ID 令牌是否打算在之前提供给您的应用程序和“过期时间”声明,以验证 ID 令牌未过期颁发者声明,以验证令牌是由v2.0 端点随机数,作为令牌重放攻击缓解建议您使用标准库方法,如 JwtSecurityTokenHandler.ValidateToken 方法 (JwtSecurityToken) 来完成上述大部分繁重工作。您可以通过根据令牌中收到的声明做出决策来进一步扩展验证过程。例如,多租户应用程序可以通过针对一组预先选择的租户检查 tid 声明(租户 ID)的值来扩展标准验证,以确保他们只尊重他们选择的租户的令牌。

  1. 示例访问令牌,仅用于理解:访问令牌和 Id_token 都是简单的 base64 编码 JSON Web 令牌 (JWT)。您可以对这些内容进行解码以查找声明,然后对其进行验证。我正在分享一个示例,其中包含执行此操作的代码。在此之前,这里是来自 Microsoft Docs 的示例访问令牌。我只是从这里拿了一个例子

实际值:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1N...(长编码字符串继续)解码值(您可以使用https://jwt.io等网站轻松检查):

{
  "aud": "https://service.contoso.com/",
  "iss": "https://sts.windows.net/7fe81447-da57-4385-becb-6de57f21477e/",
  "iat": 1388440863,
  "nbf": 1388440863,
  "exp": 1388444763,
  "ver": "1.0",
  "tid": "7fe81447-da57-4385-becb-6de57f21477e",
  "oid": "68389ae2-62fa-4b18-91fe-53dd109d74f5",
  "upn": "frankm@contoso.com",
  "unique_name": "frankm@contoso.com",
  "sub": "deNqIj9IOE9PWJWbHsftXt2EabPVl0Cj8QAmefRLV98",
  "family_name": "Miller",
  "given_name": "Frank",
  "appid": "2d4d11a2-f814-46a7-890a-274a72a7309e",
  "appidacr": "0",
  "scp": "user_impersonation",
  "acr": "1"
}

如您所见,解码后的值有许多声明,包括您将要验证的“tid”。

希望这可以帮助!


推荐阅读