Azure B2C token validation in API returns 401 Unauthorized due to invalid signature

MT 6 Reputation points
2021-10-13T18:54:21.967+00:00

I am trying to set up a web app and web API using Azure B2C as the auth mechanism, but apparently failing miserably. My web app and API are both registered in Azure, the return URIs set properly, a scope created for the API which is then granted to the app, an app client secret created, and I have tested this locally WITH IT WORKING, but now that I've published my API to my live Azure App Service, it goes wrong for some reason.

Locally, I have run my web API and web app separately, signed into the web app and then obtained an access token, called the API (running locally) and all works well. However, now the API is published and running live (i.e. https://api.myapp.net), I am running the web app locally, calling the live API and every single time I request an access token then send it to the API I get a 401 Unauthorized because apparently the signature is invalid. WHY?!?!?!?

Below are slightly redacted copies of my startup and app settings files. Note I also have Azure Front Door set up to redirect "login.myapp.net" to "myapp.b2clogin.com", this has been tested with both the user flows on the Azure dashboard and with my own app running locally and all is fine.

Here is my web app's Startup.cs file:

   services.AddRazorPages();  
     
   services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)  
       .AddMicrosoftIdentityWebApp(Configuration.GetSection(Constants.AzureAdB2C))  
       .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "https://myapp.net/api/query" })  
       .AddInMemoryTokenCaches();  
     
   services  
       .AddControllersWithViews()  
       .AddMicrosoftIdentityUI();  
     
   services  
       .AddServerSideBlazor()  
       .AddMicrosoftIdentityConsentHandler();  
     
   services.AddAuthorization(authorizationOptions =>  
   {  
       authorizationOptions.AddPolicy(  
           App.Policies.CanManageUsers,  
           App.Policies.CanManageUsersPolicy());  
   });  
     
   services.Configure
Microsoft Entra ID
Microsoft Entra ID
A Microsoft Entra identity service that provides identity management and access control capabilities. Replaces Azure Active Directory.
19,455 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Rob Dickinson 11 Reputation points
    2022-03-24T11:13:51.543+00:00

    I'm not sure this answers your particular problem but I recently came across the same (or a very similar) problem when using Azure AD B2C to sign in users and retrieve a Jwt access token to use as a bearer token providing access to the API's in my application. The reason I cannot be sure it's the same problem is that our applications do not use any of the Microsoft libraries, and instead use either Spring Security in Java or React components supporting OAuth2.0.

    I found the problem to be as follows:

    • The authorisation and token endpoints are published in paths under of https://<b2c-tenant-identifier>.b2clogin.com/<b2c-tenant-identifier>.onmicrosoft.com/<policy-name>
    • Similarly, the metadata endpoint is published under the same location https://<b2c-tenant-identifier>.b2clogin.com/<b2c-tenant-identifier>.onmicrosoft.com/<policy-name>/v2.0/.well-known/openid-configuration

    When I use these endpoints to retrieve an access token, the "issuer" claim in the token is in the form: https://login.microsoftonline.com/<b2c-tenant-guid>/v2.0

    The standard Jwt decoder implementation in Spring security will then look for metadata in various paths under the ISSUER location - as specified by https://datatracker.ietf.org/doc/html/rfc8414#section-3

    As the metadata is not located under this issuer location, it cannot find it, and therefore cannot find the keys required to validate the signature of the Jwt.

    I solved this problem by configuring the JWKS_URL for my Azure B2C tenant directly into my application, and specifying this location to the decoder rather than expecting it to discover the URL from the metadata.

    Hope this helps anyone struggling with the same problem.

    2 people found this answer helpful.
    0 comments No comments