DEV Community

Christos Matskas for The 425 Show

Posted on • Edited on

Secure Minimal APIs with .NET 6 and Azure AD B2C

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 so if you prefer video over text, you can watch all the action on our YouTube channel:

Prerequisites

You'll need the following to follow along:

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

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

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, 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

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
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

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

Alt Text

The information we need from this App Registration is:

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
We can now test it out. If all is configured correctly, you should see the following:

Alt Text

Create and secure the minimal API

Open your favorite terminal (mine is Windows Terminal) and type the following:

dotnet new webapi --auth IndividualB2C
Enter fullscreen mode Exit fullscreen mode

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();

Enter fullscreen mode Exit fullscreen mode

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": "*"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Show me the Code!!

If you want to go straight to the source code, check out our working sample in the 425Show Github repo

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.

Top comments (5)

Collapse
 
leszekkalibrate profile image
LeszekKalibrate

Hi,
Could you fix links to images please?

Collapse
 
christosmatskas profile image
Christos Matskas

Hmmm, all images are working for me

Collapse
 
leszekkalibrate profile image
LeszekKalibrate

I see a lot of ALT TEXT and 404 in Network console. (EDGE browser)

Request URL: res.cloudinary.com/practicaldev/im...
Request Method: GET
Status Code: 404 Not Found

Thread Thread
 
christosmatskas profile image
Christos Matskas

Unfortunately this is a hosted service and the images are hosted on an S3 bucket that dev.to manages. No idea why the images here would be failing :(

Collapse
 
idusortus profile image
Sam Johnson

Thanks man! Really appreciate the knowledge you share with the developer community.