Client-Server Architecture using Azure AD B2C and Containers

Summary

This post demonstrated a simplified Client-Server Architecture using Azure B2C and Containers. The frontend application consists of a single page demonstrating three stages:

  1. Front-end request for a JWT from the Azure AD OAuth 2.0 token endpoint (v2) found in the App registration.
  2. Front-end Post Request to the Backend service with a response from the Back-end service.
  3. Front-end Get Request to the Backend service with a response from the Back-end service.

Architecture

The Client-Server architecture will demonstrate communication between Front-end and Back-end Web Applications hosted in Linux Containers. Both the Front-end and Back-end applications will authorise against Azure B2C demonstrating separations of concern.

This page only summarises the complete architecture. The full code can be found in my GitHub repository.

Technologies used

  1. Azure AD B2C – will be used to provide Authorisation and User Registration Management.
  2. Azure Container Registry (ACR) – ACR will be used to store images. These images will be deployed to Azure Containers.
  3. Azure Container Apps – the compute service used to manage both the Front-end and Back-end services.
  4. ASP NET Core Web API – the Front-end will be built on ASP .NET Core, and the Back-end built with ASP .NET WEB API.

Azure B2C

Two Application Registrations will need to be created; Back-end and Front-end. The Back-end Application will need to Expose API. Scopes will need to be created so that the Front-end application can consumes the API.

Back-end App Registration

The following steps need to be carried out when registering a Back-end Application.

  1. Register the App and enable the creation of tokens.
  2. Expose an API

Create a new App Registration

Create a new Application Registration called Back-end. Enable the following two authentications methods which will be used to send an authorisation token to the Front-end application.

  • Access tokens (used for implicit flows)
  • ID tokens (used for implicit and hybrid flows)

Expose an API

Generate a new Application ID URI by clicking on the set button. This will create a new App Registration Endpoint.

Add new Scopes. Scopes are defined in the Web API C# project.

Keep a note of the Application ID URI, this will be required later.

https://diskussio.onmicrosoft.com/3657a273-3560-4c46-a647-b949ead0d43c

Front-end App Registration

The following steps need to be carried out when registering a Front-end Application.

  1. Register the App and enable the creation of tokens.
  2. Create a new App Secret.
  3. Enable API Permissions.

Create a new App Registration

Create a new App Registration called Client. Make a note of the Application (client) ID. This will be needed later.

3df5d6eb-9b2d-488f-b587-8be16d300595

Create a new Client Secret

  1. Create a new Client secret.
  2. Keep the secret value safe, this will be required later.
XR_8Q~Q-_ZvALLEze6FYf2LbVACffjJ8q~i2Mar.

API Permissions

The Client App will need access to the APP APIs. Add the permissions here.

  1. Click Add a permission
  2. APIs my organisation uses
  3. Click APP

After adding the APP you should see something similar to the list below.

Finally, click to enable admin consent by clicking Grant admin consent. In the above screen, Admin consent has been granted.

Client Token Endpoint

In the Client App, go to Endpoints and note the Azure AD OAuth 2.0 token endpoint (v2).

Following the steps above you should have the following details listed:

grant_type: client_credentials
client_id: 3df5d6eb-9b2d-488f-b587-8be16d300595
client_secret: XR_8Q~Q-_ZvALLEze6FYf2LbVACffjJ8q~i2Mar.
scope: https://diskussio.onmicrosoft.com/3657a273-3560-4c46-a647-b949ead0d43c/.default

Change Scopes

Azure AD v2.0 works on scopes where previously resources may have been requested in Azure AD v1.0. If you want to avoid setting scopes for your controller, add the line:

"AllowWebApiToBeAuthorizedByACL": true
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "diskussio.onmicrosoft.com",
    "TenantId": "cf1668e7-f722-4a8c-a051-09c6beaab473",
    "ClientId": "3657a273-3560-4c46-a647-b949ead0d43c",
    "AllowWebApiToBeAuthorizedByACL": true
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Back-end Service

The backend service needs the controller class updated to enable Authorisation.

Startup.cs/Program.cs

using Web_API.Models;	
using Microsoft.EntityFrameworkCore;	
var builder = WebApplication.CreateBuilder(args);	
// Add services to the container.	
builder.Services.AddControllers();	
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle	
builder.Services.AddEndpointsApiExplorer();	
builder.Services.AddDbContext<TodoContext>(opt =>	
    opt.UseInMemoryDatabase("TodoList"));	
builder.Services.AddSwaggerGen();	
var app = builder.Build();	
// Configure the HTTP request pipeline.	
if (app.Environment.IsDevelopment())	
{	
    app.UseDeveloperExceptionPage();	
    app.UseSwagger();	
    app.UseSwaggerUI();	
}	
else if (app.Environment.IsProduction())	
{	
    app.UseSwagger();	
    app.UseSwaggerUI();	
}	
app.UseHttpsRedirection();	
app.UseAuthorization();	
app.MapControllers();	
app.Run();
using Web_API.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddDbContext<TodoContext>(opt =>
    opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI();
}
else if (app.Environment.IsProduction())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}


app.UseHttpsRedirection();

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

app.MapControllers();

app.Run();

Controller.cs

 [Route("api/[controller]")]
    [ApiController]
 [Route("api/[controller]")]
    [ApiController]
    [Authorize]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]

Deploy to Azure Container Registry

Deploy both the Fron-end and the Back-end to the Azure Container Registry. Once deployed, you should be able to use these images to deploy the apps to Azure Container Apps.