2
votes

Scenario: I have a Blazor wasm app secured with B2C Authentication that needs to call an HTTP triggered Azure function. What would be the best method to secure that Azure function so that only the Blazor app and/or authenticated users could call that function?

So far I know how to secure the Blazor app with B2C (obviously silly!) and I've also been able to add B2C auth to an Azure function and secure the calls by validating the jwt token. But it's not clear in my head how the two parts should jive together.

Should I expose an API in the app registration of the Azure Function in the B2C tenant? If so, how the Blazor app would make authenticated calls to the Azure function?

Or do I simply send the jwt token from the Blazor app through the http request headers of the Azure function call and then validate that token manually inside the function?

I've been reading a lot of different posts on the subject lately but I still can't figure out what's THE best solution to achieve it.

Any help/cues would be appreciated.

Thanks!

ps: I'm not interested in using the Azure API management since it's a little bit on the pricey side for a pretty simple app solution.

1
Do you have any update?Jim Xu
Hi @jim-xu ! First, thank you for the detailed answer. I've just started trying to make it work yesterday. I've been struggling with the new App registration vs the legacy one in the b2c portal. Also, I cannot use the latest version of Microsoft.Extensions.Http (5.0) it seems to break other parts of the app. I'll keep you posted. :)FPIMarc

1 Answers

2
votes

If you want to call Azure function projected by Azure AD B2C, please refer to the following steps

  • Configure Azure AD B2C for Azure function

    1. Create Azure B2C app.

      Web App/API : Yes

      Allow Implicit Flow : Yes

    2. Set Reply URL in B2C app: https://{function app url}/.auth/login/aad/callback

    3. Set App ID URL in B2C App : https://{tennat}/{prefix}

    4. Note down B2C apps Application ID.

    5. Define API scope. Go to B2C app => Published scopes.

    6. Get your B2C user flows/policy’s metadata URL. Note down this URL.

      It can be obtained from Run User Flow page.

      It’s format is like https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}.

    7. Go to your functions => Authentication / Authorization.

    8. Set following

      • App Service Authentication : On
      • Action to take when not authenticated : Login with Azure AD
      • Authentication providers : Azure AAD
      • Management Mode : Advanced
      • Client Id : {Application Id from Step 4}
      • Issuer URL : {URL from step 6}
      • Allowed Audience: {Application Id URL from Step 3}
  • Create Client application For more details, please refer to here.

  • Configure CORS policy in Azure Function enter image description here

  • Configure Application

  1. Create
     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. Code
  • Create Custom AuthorizationMessageHandler class

    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>" });
        }
    }
  • Add the following code in 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();
        }
  • Call the 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");
              }


           }

enter image description here