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:
- Front-end request for a JWT from the Azure AD OAuth 2.0 token endpoint (v2) found in the App registration.
- Front-end Post Request to the Backend service with a response from the Back-end service.
- 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
- Azure AD B2C – will be used to provide Authorisation and User Registration Management.
- Azure Container Registry (ACR) – ACR will be used to store images. These images will be deployed to Azure Containers.
- Azure Container Apps – the compute service used to manage both the Front-end and Back-end services.
- 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.
- Register the App and enable the creation of tokens.
- 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.
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.
- Register the App and enable the creation of tokens.
- Create a new App Secret.
- 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
- Create a new Client secret.
- 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.
- Click Add a permission
- APIs my organisation uses
- 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.