【问题标题】:Azure SQL authenticate as user via API with MS Identity PlatformAzure SQL 使用 MS 身份平台通过 API 以用户身份进行身份验证
【发布时间】:2021-04-18 23:30:16
【问题描述】:

我有一个 ASP.NET Core 3.1 Web 应用程序调用一个 ASP.NET Core 3.1 Web API,而后者又访问一个 Azure SQL 数据库。身份验证通过 MSAL(Microsoft 身份平台)提供 - 即使用相对较新的 Microsoft.Identity.WebMicrosoft.Identity.Web.UI 库。

目标是确保用户在自己登录的上下文下通过 API 从 SQL 中拉取数据,从而实现行级安全、访问审计和其他好处。

我已成功让登录过程为 Web 应用程序工作 - 并通过它获得一个有效的访问令牌,以使用我在向 AD 注册后者时创建的范围访问 API。

当我从 Visual Studio 本地运行 API 和应用程序时,一切都按预期工作 - 为应用程序提供了正确的访问令牌以访问 API,并提供 API 以访问 SQL - 在这两种情况下都在用户的(即我的)身份。

但是,当我将 API 发布到 Azure 上的应用服务,并从本地版本的 Web 应用或应用服务托管的版本访问它时,API 获取访问 SQL 的访问令牌包含API 的应用程序身份(系统分配的托管身份),而不是用户的身份。虽然我可以作为应用程序访问 SQL,但这不是我们需要的。

Web 应用使用ITokenAcquisitionGetAccessTokenForUserAsync 方法获取其访问令牌 - 将我为 API 定义的单个范围作为参数。

API 获取其令牌(以访问 SQL),如下所示:

var token = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net", _tenantId)

...其中 _tenantId 是订阅的租户 ID。

我已将 SQL Azure 数据库“user_impersonation”API 权限添加到 API 的 AD 注册中 - 但这并没有帮助。顺便说一句,出于某种原因,Azure 将此权限的全名命名为 https://sql.azuresynapse.usgovcloudapi.net/user_impersonation - 这有点令人担忧,因为这只是一个位于英国的常规 Azure 帐户。

我发现了一些与此类似的帖子,但主要针对旧版本的解决方案集。我希望避免编写自己的代码来发布令牌请求 - 这应该由 MSAL 库处理。

我是否应该在登录后以某种方式从 Web 应用程序单独请求 SQL 访问范围,或者 API 是否应该做一些不同的事情来获取标识用户的 SQL 访问令牌?为什么在本地运行时完美运行?

这似乎应该是一个非常常见的用例(最常见的?),但几乎没有记录 - 我发现的大多数文档仅涉及正在使用的应用程序身份,或者没有告诉您要做什么这个特殊的技术栈。

【问题讨论】:

    标签: asp.net-core impersonation msal microsoft-identity-platform azure-sql


    【解决方案1】:

    终于 - 成功了!最后,这是文档的关键部分:Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow - 关键点是:

    1. 应用只要求令牌来访问 API。
    2. 然后,API 代表通过第一个令牌识别的用户请求令牌以访问 SQL。

    关键是 - 由于 API 无法触发第二步的同意窗口 - 我必须使用 Azure 门户中的 Enterprise Applications 选项卡来预先授予 SQL 权限。

    所以好消息是它确实有效:也许这对某些人来说是显而易见的,但 IMO 花了很长时间才找到答案。我会在适当的时候写一个关于如何做到这一点的更完整的解释,因为这不仅仅是我在努力解决这个问题。

    坏消息是 - 在我的调查过程中 - 我发现 Azure B2C(这是我需要添加的下一个东西)不支持这种“代表”流程 - click here for details。这真是太可惜了,因为我认为这是它最明显的用例!哦,好吧,回到绘图板。

    【讨论】:

      【解决方案2】:

      我目前正在使用 Net5.0 Web 应用程序解决类似的问题。它似乎在本地工作的原因是您使用可以访问 Azure SQL 的用户登录到 Visual Studio,这些是您在 Db 中获得的权限。 IDE 使用这些凭据代替托管服务标识,后者在您将应用程序上传到 Azure 时使用。

      如您所述,在应用注册中,您需要为 Azure SQL 数据库 user_impersonation 授予应用权限。

      在您的代码中,您需要从https://database.windows.net//.default 请求一个令牌(注意 // 因为它是 v1 端点所必需的)。通过引用 /.default,您要求您在应用注册门户中为应用选择的所有权限。

      https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#the-default-scope

      在 Startup.cs 中,您需要在所需范围内启用 EnableTokenAcquisitionToCallDownstreamApi。

              services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
                  .EnableTokenAcquisitionToCallDownstreamApi(new[]
                     {"https://database.windows.net//.default"})
                  // Adds the User and App InMemory Token Cache 
                  .AddInMemoryTokenCaches();
      
              services.AddAuthorization(options =>
              {
                  // By default, all incoming requests will be authorized according to the 
                  // default policy
                  options.FallbackPolicy = options.DefaultPolicy;
              });
      
              services.AddDbContext<MyDatabaseContext>(options =>
                     options.UseSqlServer(
                     Configuration.GetConnectionString("MyAzureConnection")));
      
              // The database interface
              services.AddScoped<ITodos, TodoData>();
      
              services.AddRazorPages()
                  .AddRazorRuntimeCompilation()
                  .AddMvcOptions(o => 
                  {
                      var policy = new AuthorizationPolicyBuilder()
                      .RequireAuthenticatedUser()
                      .Build();
                      o.Filters.Add(new AuthorizeFilter(policy));
                  })
                  .AddMicrosoftIdentityUI();
      

      您还需要使用[AuthorizeForScopes(Scopes = new string[]{"https://database.windows.net//.default"}] 装饰您的控制器,并包含该控制器所需的范围。对于 Razor,它位于页面模型的顶部,需要引用 `using Microsoft.Identity.Web;'

      namespace ToDoApp.Pages.Todos
      {
          [AuthorizeForScopes(ScopeKeySection = "AzureSQL:BaseUrl")]
          public class CreateModel : PageModel
      

      我在 appsettings.json 中使用一个部分作为范围,并使用 ScopeKeySection 检索它:

        "AzureSQL": {
          "BaseUrl": "https://database.windows.net//.default",
          "Scopes": "user_impersonation"
        }
      

      这向您展示了将它包含在 MVC、Razor 和 Blazor 的位置:

      https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access#in-mvc-controllers

      最后,您的 DbContext 需要一个令牌,您可以将其从客户端应用程序传递给它(也许......)。

      这就是我现在的做法

          public class MyDatabaseContext : DbContext
          {
              private readonly ITokenAcquisition _tokenAcquisition;
      
              public MyDatabaseContext (ITokenAcquisition tokenAcquisition,
                                  DbContextOptions<MyDatabaseContext> options)
                  : base(options)
              {
      
                  _tokenAcquisition = tokenAcquisition;
                  string[] scopes = new[]{"https://database.windows.net//.default"};
      
                  var result = _tokenAcquisition.GetAuthenticationResultForUserAsync(scopes)
                                  .GetAwaiter()
                                  .GetResult();
                  token = result.AccessToken;
      
                  var connection = (SqlConnection)Database.GetDbConnection();
                  connection.AccessToken = result.token;
              }
      

      这是一个有缺陷的解决方案。如果我重新启动应用程序并尝试再次访问它,我会收到错误 Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user

      好像和TokenCache有关。如果我退出并再次登录或清除我的浏览器缓存,错误就会得到解决。我有一个解决方法,可以在失败时登录应用程序,但由于我使用的是应用程序的凭据,所以它有缺陷。

      但是,它以用户而不是具有用户权限的应用程序成功连接到 Azure SQL Db。当我解决错误(或找到错误)时,我会更新这个答案。

      【讨论】:

      • 感谢您的回复:您是在上述场景中直接从最终用户应用程序访问数据库吗?我的问题是我需要“链接” - 即应用程序访问 api,api 访问 SQL。我可以在应用程序中获得访问 SQL 或访问 API 的授权 - 但似乎无法同时获得两者(如果我添加两个范围,我会收到错误“.default 范围不能与特定于资源的组合范围” - 这非常无益)。在我的设置中,API 正在从 APP 获取 OpenID 令牌 - 所以它知道用户是谁:但它可以代表用户获取 SQL 令牌吗?
      • 我得出的结论是,MS 从未考虑过任何人都希望在 3 层应用程序中为用户使用 SQL 安全性的可能性。这是非凡的。我刚刚看到 Azure AD B2C 明确不支持“代表”流程 - 但我没有看到任何证据表明常规 Azure 也这样做。我能找到的每一篇文章和教程都谈到了使用“托管身份”识别应用程序 - 没有提到识别用户。我想我必须将用户 ID 作为参数传递给每个 SQL 调用,并编写显式代码来处理安全性。远非理想。
      • 是的,它在前端应用程序中是正确的。我猜你需要同意访问 Azure SQL 到 Api,将用户的令牌从前端应用程序传递到 api 以使用并从数据库请求权限。还要确保在 AAD 中授予管理员同意。
      • 你看过这个样本吗? github.com/Azure-Samples/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-16
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 2016-04-12
      相关资源
      最近更新 更多