Logical API for multi-API access, does not work

Hi
I’ve got as follows

“Client App” - this is a Winforms NET 6.0 desktop application. I’ve used Auth0.OidcClient.Core and built a web-based Auth0 login experience into this app. This works fine. I get an access token on a successful login, and a refresh token which my code uses to do a refresh and get a replacement access token at regular intervals.

“Api A” - this is an ASP NET Core 6.0 Web API which I have deployed to Azure. This uses the Microsoft.AspNetCore.Authentication.JwtBearer. The configuration code is shown below. This API is registered in Auth0 and I can use my Client App token to call the API. So far, so good.

Now I have created “Api B” and this is where the problem begins. I want to use my Client App access token to call both Api A and Api B. Api B is, like Api A, an ASP NET Core 6.0 Web API, and both APIs are configured to use JwtBearer authentication. Across both APIs, controllers have the [Authorize] attribute, but nothing more (no authentication “Policy”, for example).

I have reviewed this article, which appears to specifically address my scenario: Configure Logical API for Multiple APIs

So, I have taken the following steps:

  1. Registered API B in Auth0

  2. Registered a new logical API, let’s call it “API L”, in Auth0

  3. As I have no existing Scopes, but the article says I need them to support this scenario, I created new scopes as follows

-In the Permissions tab for API A, I created a new permission called “api:APIA”, intended to represent permission to access API A and all its actions and resources
-In the Permissions tab for API B, I created a new permission called “api:APIB”, intended to represent permission to access API B and all its actions and resources
-In the Permissions tab for API L, I created a new permission called “api:APIA”, matching the above
-In the Permissions tab for API L, I created a new permission called “api:APIB”, matching the above

  1. Modified my client code to send a login request with the audience for “API L”, and with the new Scopes “api:APIA api:APIB” included in the request.

I found the section of the article which talks about Scopes to be unclear. However My working assumption is that because I now have scopes in API L (“api:APIA”, “api:APIB”) which have identical names to the scopes declared for each of API A (“api:APIA”) and API B (“api:APIB”), Auth0 will then do the necessary magic that allows a token issued for the Logical API to be used to access the other two APIs.

I have not changed the Audience and Domain configuration on the ASP/Azure side of API A or API B. I presume I shouldn’t need to do so in order to make this work.

However, now, API A will not authorise a token issued to my Client App. It returns an “Unauthorised” error.

I am trying to work out what I’m missing.

I did wonder if the fact that the token includes any scope at all (whereas in my previous configuration, there were no scopes defined) might be enough to prevent my API A from authenticating successfully, without making some kind of configuration change to the API. Perhaps I have to instruct the API explicitly to disregard Scopes?

To be clear, I don’t need scopes at all. I just want my client to be able to authenticate to both APIs, using the same token, to access the full functionality of both APIs. However my presumption is that the scopes are necessary to create the magic glue between a “logical api” and a set of concrete APIs.

I would appreciate any pointers here about what i might be doing wrong??

Config code for API A

var builder = WebApplication.CreateBuilder(args);

var GoogleTextSpeechApiKey = builder.Configuration[“GoogleTextSpeech:apikey”];

builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = $“https://{builder.Configuration[“Auth0:Domain”]}/”;
options.Audience = builder.Configuration[“Auth0:Audience”];
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = ClaimTypes.NameIdentifier
};
});

builder.Services.AddControllers();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();

I have not changed the Audience and Domain configuration on the ASP/Azure side of API A or API B. I presume I shouldn’t need to do so in order to make this work.

@robertellisuk Why do you assume this? I thought that’s exactly what needs to be done in such a scenario. Could you elaborate, please? I’m facing a similar scenario at the moment.

I made that assumption because to my mind, if the inverse assumption held true, there would be no benefit in having a logical api as a single entity for implementation of multiple ‘concrete’ APIs. In other words, my thinking was, ‘if I have to update the configuration of all of the composite APIs, then what is the point of the Logical API to begin with?’

Anyway, as it turned out you are quite right to question this assumption, and my thinking must have been awry, because that was the key to making it work. I had to add the logical API audience to each of the discrete APIs.

I’m still not sure I fully understand much of this (OAuth) to be honest. It’s genuinely difficult stuff.

Good luck!

I think that’s the whole point of the Logical API - that you kind of merge the configuration of all your underlying APIs into one. To be fair, I also don’t see too many benefits of this approach. It seems it just solves the issue where you need to generate another access token (to access a different API, so you have different level of access basically) but you use refresh tokens at the same time. It looks like the refresh token is bound to single audience only, so every time you try to create another token, with different audience it will just fail.

I’m not 100% sure I’m making any sense now either, frankly. Maybe someone from Auth0 team will confirm.