Objective of this API is to provide 3 "complex" operations (x^y, %, !) as authenticathed API. In order to be implemented, these APIs require calls to Calculator Api. Both API are registered as Azure B2C Applications, and we want configure averything so that this API re-use the bearer provided by the caller to authenticate the target API. This means that we are impersonating the calling when we call the target API.
The configuration is very similar to ApiCalculator, so I will highlight all relevant differences.
Create an application on Azure B2C.
- Name: ApiScientificCalculator
- WebApp/Include WebAPI: YES
- WebApp/allow Implicit Flow: YES
- Reply URL: https://jwt.ms
After creating it, select "Api access". Click the "Add" button. In the next blade, add both ApiCalculator and ApiScientificCalculator) this will allow to call with the same authorization bearer both ApiScientificCalculator and ApiCalculator.
Via visual studio create a solution of type "ASP.NET Core Web Application", type API, with NO Authentication
Via Visual Studio create a solution of type "ASP.NET Core Web Application", type API, with NO Authentication.
You don't need to create an additional policy, just use the policy already created (B2C_1_signin-default).
In appsettings.config add the following information:
"AzureAdB2C": {
"Tenant": "nicolb2c.onmicrosoft.com",
"ClientId": "<the application id (guid)>",
"Policy": "B2C_1_signin-default"
},
also add following NuGet packages to the solution
Microsoft.AspNetCore.Authentication.JwtBearer
Microsoft.AspNetCore.Mvc
Microsoft.Extensions.Configuration.UserSecrets
Microsoft.Extensions.SecretManager.Tools
Update program.cs and startup.cs as shown in this repository, then create a controller for the home page Controllers/HomeController.cs with the corresponding view View/Home/index.shtml.
It is now the time to build the real API. We will implement the API with the controller Controllers/CalcController.cs****
The sequence of operation needed to authenticate the API is similar to APICalculatator. The interesting part is the API to API portion of the code. Here the main attention points:
public async Task<IActionResult> Get(string op, [FromQuery] double param1, [FromQuery] double param2)
var header_bearer = HttpContext.Request.Headers["Authorization"];
string bearer = header_bearer.FirstOrDefault().Split(" ")[1];
string CALL_MULTIPLY = "http://nicolapicalculator.azurewebsites.net/api/calc/sum?param1={0}¶m2={1}";
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearer);
var httpResponse = await _client.GetAsync(string.Format(CALL_MULTIPLY, param1, param2));
By default Microsoft.AspNetCore.Authentication.JwtBearer middleware verifies if the token and the API audience match. This means that in a cross API call, that happens when WebCalculator calls ApiScientificCalculator using the same bearer, you will receive the following message:
AuthenticationFailed: IDX10214: Audience validation failed. Audiences: '<guid>'. Did not match: validationParameters.ValidAudience: '<guid>' or validationParameters.ValidAudiences: 'null'.
This because both clientid "WebCalculator" and clientid "APIScientificCalculator" want to access to same API (APICalculator).
In order to avoid this, in calculatorAPI>Startup>ConfigureServices you need to add the following
var tokenValidationParameters = new TokenValidationParameters
{
RequireExpirationTime = true,
RequireSignedTokens = true,
SaveSigninToken = false,
ValidateActor = false,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = false,
ValidateLifetime = true,
ValidAudiences = new string[] {
Configuration["AzureAdB2C:ClientId"], // API Scientific Calculator
"c07391de-3205-4496-a704-4607b18b64f9", // WebCalculator
"d668afda-f613-43f7-89e4-5425496ebdf2", // postman
}
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(jwtOptions =>
{
jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AzureAdB2C:Tenant"]}/{Configuration["AzureAdB2C:Policy"]}/v2.0/";
jwtOptions.Audience = Configuration["AzureAdB2C:ClientId"];
jwtOptions.TokenValidationParameters = tokenValidationParameters; // ADD THIS LINE TOO
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
};
});
where you are telling "dear middlewhere, please do not validate Audience on each call":)
That's all. In order to call the API, you can go to Azure Portal > Azure B2C > Policies > Sign-up or Sign-In User Policy > B2C_1_signin-default
- Application: ApiScientificCalculator
- ReplyURL: https://jwt.ws
Click [RUN NOW], copy the bearer from the page and use it in Postman.