question

Olive-1686 avatar image
0 Votes"
Olive-1686 asked JitendraRai-2073 answered

B2C Authenticated Azure Function returns 401 Unauthorized

I'm trying to secure an Azure Function app with B2C and then use a .Net 6 Blazor Webassembly app to log into a B2C Microsoft account and be able to call a function in the secure Azure Function app. However I'm having no luck with getting this to work. I've been able to solve every problem so far after extensive Googling, however I've finally hit a wall I cannot solve.

Let me step through my set up in the hopes that someone can spot what I'm doing wrong or what I might be missing.

Firstly, I've created two B2C app registrations, one for the Blazor WebAssemmbly app and one for the Azure Function Api app.

125282-1.png

Within the Api app registration, I've set up a Web authentication with a redirect URL for the Azure Function app. I've got ID Tokens ticked as that is what I read somewhere, however I'm wondering if that is correct? As from what I understand .Net 6 uses MSAL 2.0, which uses Authorization Code Flow with PKCE rather than implicit flow.

125267-2.png

Then I set the Application ID URL and created a "user_impersonation" scope.

125256-3.png

Over in the Blazor App registration, I have the authentication set up like this.

125303-4.png

The Api Permissions are set to consent the "user_impersonation" scope from the Api app registration.

125292-5.png

In the Azure Function app itself I have set up the Authentication to use the Microsoft identity and the B2C Api app registration.

125247-6.png

I've also set the Allowed token audiences to match the Application ID URL of the B2C Api app.

125248-7.png

Note also a client secret was automatically created in the B2C Api app

125293-9.png

Which has been set in the Azure Function app.

125321-8.png

The Blazor app registration also has a user flow set up with the Microsoft identity platform. I can run this user flow and log in successfully.

I can also jump over to the Azure Function app and try to run one of the functions. It will automatically redirect me to the Microsoft identity login page, after which I can log in and the function will run successfully.

Now, over in the .Net 6 Blazor Webassembly app, I have the appsettings.json set as follows.

   "AzureAdB2C": {
     "Authority": "https://#######.b2clogin.com/########.onmicrosoft.com/B2C_1_signupsignin",
     "ClientId": "063#########################6ef",
     "ValidateAuthority": false
   }

I then have a custom authorization message handler like this.

 public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
 {
     public CustomAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigationManager) : base(provider, navigationManager)
     {
         ConfigureHandler(
             authorizedUrls: new[] { "https://######.azurewebsites.net/" },
             scopes: new[] { "https://########.onmicrosoft.com/c32########################532/user_impersonation" });
     }
 }

Then in Program.cs I have the following.

 builder.Services.AddHttpClient("Api", client => client.BaseAddress = new Uri("https://#######.azurewebsites.net/")).AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
    
 builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Api"));
    
 builder.Services.AddMsalAuthentication(options =>
 {
     builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
     options.ProviderOptions.DefaultAccessTokenScopes.Add("https://#######.onmicrosoft.com/c32##########################532/user_impersonation");
    
     options.ProviderOptions.LoginMode = "redirect";
 });

Finally, I'm calling a function from the Azure Function Api app like this on the Counter page. I have a button that calls this function when clicked, but it won't let me paste that part of the HTML code here for some reason.

 code {
     private string test;
     protected async Task CallFun()
     {
         test = await Http.GetStringAsync("api/Run");
     }
 }

Now I fire up Fiddler (to catch the call traffic) before I run this Blazor Webassembly app, I can click the login button, which redirects me to the Microsoft login page. From then I can successfully login, and it then shows my name up the top after logging in. I then click over to the Counter page (where the above test code is) and click the Test button. After a few seconds I get the error message at the bottom telling me to reload the page. Upon inspecting the error in the browser's dev tools, I can see it has returned a 401 unauthorized.

So I jump over to fiddler and inspect the call there. It too says 401 unauthorized and the text response says "You do not have permission to view this directory or page." However inspecting the header shows the Bearer token is attached correctly. If I copy the Bearer token and paste it into jwt.ms, it is decoded and appears correct. If shows the I have the user_impersonation scope.

So now I'm totally stuck and don't know where to turn or what to look at next.

azure-functionsazure-ad-b2cdotnet-aspnet-core-blazor
1.png (12.9 KiB)
2.png (68.7 KiB)
3.png (26.1 KiB)
4.png (87.9 KiB)
5.png (20.1 KiB)
6.png (23.2 KiB)
7.png (26.4 KiB)
8.png (70.2 KiB)
9.png (8.3 KiB)
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

JitendraRai-2073 avatar image
0 Votes"
JitendraRai-2073 answered

Thanks and Could you please try to change the APPID instead of user_impersonation API to the DefaultAccesTokenScope of the call in program.cs line#8.

e.g. options.ProviderOptions.LoginMode = "redirect";
options.ProviderOptions.DefaultAccessTokenScopes.Add("063xxxxxxx-xxx-xxxxx"); Please setup the actual App ID I'm using on this Blazor App. This will provide the access token.

There is more information about the 401 unauthorized access in the below URL:- https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-5.0#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client-1

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.