Secure Minimal APIs with .NET 6 and Azure AD B2C
Published: 2021-09-08 00:37:59

Minimal APIs are all the rage these days. Python and Node.js have had a much simpler model for creating APIs for a while. And now .NET 6 joins this trend - which is a welcome change. I streamed about this on our [425Show Twitch channel](https://aka.ms/425Show) so if you prefer video over text, you can watch all the action on our YouTube channel:

{% youtube wvS4emg69VA %}

##Prerequisites
You'll need the following to follow along:
- [.NET 6 Preview 7](https://dotnet.microsoft.com/download/dotnet/6.0) (as of writing this blog post)
- [Visual Studio Code](https://code.visualstudio.com/Download) (or any other .NET IDE)
- And Azure AD B2C (as part of your Azure subscription - get a free one [here](https://azure.microsoft.com/free/))

## Create the Azure AD B2C App
We'll start by creating the two App Registrations needed for our API and our API client. In the Azure AD B2C portal, navigate to the **App Registrations** blade and create a new one by clicking on the **+ New Registration**. Give it a meaningful name, leave the default for **Supported Account Types** and press **Register**. In the newly created App Registration, navigate to the **Expose an API** and Set the Application ID URI. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6agtzxr0or59wi9t1e4.png)

We can now add a **Scope**. Since we'll be pulling weather data, we can use something like `Weather.Read` for the scope name.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1ok1gxinvdeorkj7ag0.png)

Take a note of the following information:
- Application (Client) ID (App Registration Overview Tab)
- Azure AD B2C Instance name (e.g https://[YourB2CName].b2clogin.com)
- B2C Domain (e.g [YourB2CName].omicrosoft.com)
- User flow name (e.g B2C_1_susi)

## Using Thunder Client in VS Code to test a secure API
What good is it if we can't test/use our secure API? When I work with APIs, I like to use [Thunder Client](https://www.thunderclient.io/), a VS Code extension that provides a GUI based REST Client. Unlike Postman, I don't have to install it on every machine and it is available in CodeSpaces too! For Thunder Client to be able to call an Azure AD B2C secure API we we need a client API App Registration. Let's do this!

Create a new App Registration, give it a meaningful name and press **Register**. Then navigate to the **Authentication** tab and press the **+ Add a platform** button. Make sure to select **Web** and add the following for the **Redirect URI**: `https://www.thunderclient.io/oauth/callback`. This is the URL that Thunder Client is expecting to receive the Access Token. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7zxb8xu0hrix8kfmxpj.png)

Next, navigate to the **Certificates & Secrets** tab and create a new secret. Make sure to copy it as we'll need it to configure Thunder Client. 

Finally, we need to add our API permissions. Navigate to the **API permissions** tab and press the **Add a permission**. Select **My APIs**, find the name of the App Registration you created at the previous step and select it.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yb9r6ip4dhe4xp2eaiqp.png)
Expand the permissions, select the name of your permission (in your case it should be `Weather.Read`). Then press the **Add Permission** button.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dextsuvw8h17e60awqf.png)

The final, and most important, step in the process is to give  Admin Consent to our custom API Permission.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydrhyjbtms5n3rthepl0.png)

The information we need from this App Registration is:
- Application (Client) ID (App Registration Overview Tab)
- Auth endpoint (e.g https://cmatdevb2c.b2clogin.com/cmatdevb2c.onmicrosoft.com/YourB2CPolicyName/oauth2/v2.0/authorize
- Token endpoint (e.g  https://cmatdevb2c.b2clogin.com/cmatdevb2c.onmicrosoft.com/YourB2CPolicyName/oauth2/v2.0/token)
- Client Secret
- API Permission (e.g https://cmatdevb2c.onmicrosoft.com/b332fc47-cfff-4f57-bb0f-7b6ccea75521/weather.read)

In Thunder Client, create a new Request and navigate to the Auth tab, select OAuth2 and configure the settings as shown in the pic below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k5hkc9peh30yxtsijs1e.png)
We can now test it out. If all is configured correctly, you should see the following:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3z5jyzm337bcs43208nz.gif)

## Create and secure the minimal API
Open your favorite terminal (mine is Windows Terminal) and type the following:
```
dotnet new webapi --auth IndividualB2C
```
The template does most of the work for us, but we can tweak the code to make it truly minimal! Update your `Program.cs` to look like this

```
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;
using MinimalAPIWithB2C2;

string[] Summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, "AzureAdB2C");

builder.Services.AddAuthorization();
builder.Services.AddCors(options => options.AddPolicy("allowAny", o => o.AllowAnyOrigin()));
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = "MinimalAPIWithB2C2", Version = "v1" });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MinimalAPIWithB2C2 v1"));
}

app.MapGet("/weatherforecast",(HttpContext context) => 
{
    context.VerifyUserHasAnyAcceptedScope(new[] {"access_as_user"});
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}).RequireAuthorization();

app.UseAuthentication();
app.UseHttpsRedirection();
app.UseCors();
app.UseAuthorization();

app.MapControllers();

app.Run();

```
We can now delete the **Controllers** folder and all it's contents. Finally, we need to update the `appsettings.json` with the info we collected from the first step when we created the API App Registration. Your `appsettings.json` should look like this:

```
{
  "AzureAdB2C": {
    "Instance": "https://<yourB2CName>.b2clogin.com/tfp/",
    "ClientId": "<your client id>",
    "Domain": "<yourB2CName>.onmicrosoft.com",
    "SignUpSignInPolicyId": "B2C_1_susi"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
```

> The policy name needs to match in both your API and your Thunder Client settings.

Let's build and run the API so that we can test it :)

```
dotnet build
dotnet run
```

Then, let's switch to Thunder Client in VS Code and send a GET request to our API endpoint: `https://localhost:5001/weatherforecast`. If everything was configured correctly, we should receive the following output from the API:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/exr07p3ycz67l52te3i9.png)

## Show me the Code!!
If you want to go straight to the source code, check out our working sample in the [425Show Github repo](https://github.com/425show/minimalAPIWithB2C)

## Summary
I really dig the new minimal API templates in .NET 6. And although this approach may not be for everyone, I can totally see the appeal. Also note that you don't have to stick with this design and you can refactor your code out to separate files to keep your files small and concise. Controllers are here to stay! In the end, it all comes down to personal preference and what works best for you and your team.