首页 > 解决方案 > 如何使用 Azure AD B2C 保护 Blazor Wasm 应用访问的 Azure 函数?

问题描述

场景:我有一个使用 B2C 身份验证保护的 Blazor wasm 应用程序,需要调用 HTTP 触发的 Azure 函数。保护该 Azure 函数的最佳方法是什么,以便只有 Blazor 应用程序和/或经过身份验证的用户才能调用该函数?

到目前为止,我知道如何使用 B2C 保护 Blazor 应用程序(显然很愚蠢!),并且我还能够将 B2C 身份验证添加到 Azure 函数并通过验证 jwt 令牌来保护调用。但我不清楚这两个部分应该如何结合在一起。

是否应该在 B2C 租户中 Azure Function 的应用注册中公开 API?如果是这样,Blazor 应用程序将如何对 Azure 函数进行经过身份验证的调用?

还是我只是通过 Azure 函数调用的 http 请求标头从 Blazor 应用程序发送 jwt 令牌,然后在函数内手动验证该令牌?

我最近一直在阅读很多关于这个主题的不同帖子,但我仍然无法弄清楚实现它的最佳解决方案是什么。

任何帮助/提示将不胜感激。

谢谢!

ps:我对使用 Azure API 管理不感兴趣,因为它对于一个非常简单的应用程序解决方案来说有点贵。

标签: azureazure-functionsazure-ad-b2cblazor-webassembly

解决方案


如果要调用 Azure AD B2C 投射的 Azure 函数,请参考以下步骤

  • 为 Azure 功能配置 Azure AD B2C

    1. 创建 Azure B2C 应用。

      网络应用程序/API : 是

      允许隐式流:是

    2. 在 B2C 应用中设置回复 URL:https://{function app url}/.auth/login/aad/callback

    3. 在 B2C App 中设置 App ID URL:https://{tennat}/{prefix}

    4. 记下 B2C 应用程序应用程序 ID。

    5. 定义 API 范围。转到 B2C 应用 => 已发布范围

    6. 获取您的 B2C 用户流/策略的元数据 URL。记下这个网址。

      它可以从运行用户流程页面获得。

      它的格式就像https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}.

    7. 转到您的功能 =>身份验证/授权

    8. 设置以下

      • 应用服务身份验证:开启
      • 未通过身份验证时要采取的措施:使用 Azure AD 登录
      • 身份验证提供程序:Azure AAD
      • 管理模式:高级
      • 客户端 ID:{步骤 4 中的应用程序 ID}
      • 颁发者 URL:{来自第 6 步的 URL}
      • 允许的受众:{步骤 3 中的应用程序 ID URL}
  • 创建客户端应用程序 更多细节,请参考这里

  • 在 Azure Function 中配置 CORS 策略 在此处输入图像描述

  • 配置应用程序

  1. 创造
     dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "{AAD B2C INSTANCE}" --client-id "{CLIENT ID}" --domain "{TENANT DOMAIN}" -o {APP NAME} -ssp "{SIGN UP OR SIGN IN POLICY}"
    
  2. 代码
  • 创建自定义AuthorizationMessageHandler

    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
    
    public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
    {
        public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, 
            NavigationManager navigationManager)
            : base(provider, navigationManager)
        {
            ConfigureHandler(
                authorizedUrls: new[] { "<your function app url>" },
                scopes: new[] { "<the function app  API scope your define>" });
        }
    }
  • Program.cs.
public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("app");
             // register CustomAuthorizationMessageHandler 
            builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
            // configure httpclient
            // call the following code please add packageMicrosoft.Extensions.Http 3.1.0
            builder.Services.AddHttpClient("ServerAPI", client =>
              client.BaseAddress = new Uri("<your function app url>"))
                    .AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
            // register the httpclient
            builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
             .CreateClient("ServerAPI"));
            // configure Azure AD auth
            builder.Services.AddMsalAuthentication(options =>
            {
                builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
                options.ProviderOptions.DefaultAccessTokenScopes.Add("<the function app  API scope your define>");


            });

            await builder.Build().RunAsync();
        }
  • 调用 API
 @page "/fetchdata"
        @using Microsoft.AspNetCore.Components.WebAssembly.Authentication
        @inject HttpClient Http

        <h1>Call Azure Function</h1>

         <p>This component demonstrates fetching data from the server.</p>

         <p>Result: @forecasts</p>

          <button class="btn btn-primary" @onclick="CallFun">Call Function</button>

          @code {
              private string forecasts;

              protected async Task CallFun()
              {
                forecasts = await Http.GetStringAsync("api/http");
              }


           }

在此处输入图像描述


推荐阅读