DEV Community

loading...
Cover image for Testing the API waters with FeatherHTTP and Microsoft.Identity.Web
The 425 Show

Testing the API waters with FeatherHTTP and Microsoft.Identity.Web

christosmatskas profile image Christos Matskas ・6 min read

There is nothing like testing alpha or beta frameworks and getting excited about the possibilities of Open Source. So today I decided to play around with FeatherHTTP the brainchild of David Fowler - yes, that David (.NET Core, SignalR, Channels etc etc)!

Chances are you've never heard of FeatherHTTP so here's an explanation from the man himself:

A lightweight low ceremony APIs for .NET Core applications.

  • Built on the same primitives as .NET Core
  • Optimized for building HTTP APIs quickly
  • Take advantage of existing .NET Core middleware and frameworks

I really like how minimalist the framework is for creating APIs. With a shiny new tool in my hands, there was only one thing I could do: build something and slap some authentication and authorization to it while we're at it :)

Let's get started then...

Create a new FeatherHTTP project

To be able to work with FeatherHTTP, we need to add the necessary template to the .NET Core CLI - CLI only for now

dotnet new -i FeatherHttp.Templates::0.1.67-alpha.g69b43bed72 --nuget-source https://f.feedz.io/featherhttp/framework/nuget/index.json

Upon installing this template, you can use the dotnet new command to create a new project. Navigate to a new directory and run dotnet new feather. This should generate 2 files only. When we say lightweight, we mean lightweight!

Alt Text

The default code is very simple:

So far so good. Now, let's add a couple of methods to make it look more like a proper API. You will have to forgive me but I'm going to go with the standard ToDo list example but I promise to add some spice later on when we add a call to MS Graph. But baby steps first. Let's update the code to the following

We also need a ToDo class. Let's add a TodoItem.cs file to our project and add the following code:

using System.Text.Json.Serialization;

 public class TodoItem
 {
     [JsonPropertyName("id")]
     public int Id { get; set; }

     [JsonPropertyName("name")]
     public string Name { get; set; }

     [JsonPropertyName("isComplete")]
     public bool IsComplete { get; set; }
}

At this point we have a GET and a POST method to retrieve and create new ToDo items. We can test our API by launching it with dotnet run or the VS Code debugger and using Postman to make the necessary calls to http://localhost/api/todos

Alt Text

We have a lift off!!!

Create the Azure AD app registrations

For our API and API clients to be able to authenticate users and get the appropriate access tokens from Azure AD, we need to register 2 apps.

API App Registration

Go to the Azure Portal > Azure AD > App Registrations and Register a new app

Alt Text

Go to the API Permissions tab and click on the Add a Permission

Alt Text

Select Microsoft Graph and then, under Delegated permissions add the User.Read permission and press the Add permissions button

Alt Text

Ensure that you Grant admin consent as the API wont' be able to consent to these permissions - there is no user interaction, remember?

Alt Text

For the API to be accessible by other apps we need to go to the Expose our API tab and add a scope. Click on Add a scope and accept the default Application ID URI - although you can always edit this to a more readable format

Alt Text

And let's add a scope that the API client app will request when acquiring a token from Azure AD. In this instance, we will name the scope access_as_user

Alt Text

Finally, we need to create a new secret that our API will use to validate tokens and execute the On Behalf Of (the user) flow to call into MS Graph.

Go to the Certificates & Secrets tab and add a new secret

Alt Text

Make sure to copy the secret value as it's not accessible once you navigate away from this tab/page.

Client App Registration

We also need a client app that, most likely, will be one that our users can interact with (desktop app, web app etc) in order to authenticate and call the API. In the Azure Portal > Azure AD > App Registrations create a new app registration

In the Authentication tab, press the Add a platform button to configure how your app should receive the tokens from Azure AD. I will be using Postman to test my API so I chose Web and then I set the Redirect URI to: https://www.postman.com/oauth2/callback and press Save to persist my changes.

Note: We can have multiple redirect URIs so if later we decide to implement a nice front-end app, we can add the new app URIs in this space.

Finally, in API permissions, we need to configure the permission to our own API. Press the Add a permission button and select the My APIs*. Then find the one that you just created for your API and check the right permissions before you press the **Add Permissions button

Alt Text

At this point we're all set with Azure AD

Alt Text

Let's make our API secure

What could go wrong combining a beta library with an alpha version of a very promising framework? Let's find out.

To secure our API, we are going to use the new Microsoft.Identity.Web library. This library is going to GA very soon and is designed to make authentication and authorization for .NET Core web apps and apis a breeze. Let's see how we to implement this with our FeatherHTTP API.

Note: Since Microsoft.Identity.Web is still in preview, there may be some API changes coming before it hits GA

First, we need to add a reference to the Microsoft.Identity.Web NuGet package. In the *.csproj file we add the following package reference:

<PackageReference Include="Microsoft.Identity.Web" Version="0.2.1-preview" />

In addition, we need to add an appsettings.json file to store the AzureAD configuration settings as per the example below:

{
    "AzureAd": {
      "Instance": "https://login.microsoftonline.com/",
      "ClientId": "<your API app client id>",
      "ClientSecret": "<your API app secret>",
      "Domain": "cmatskas.onmicrosoft.com",
      "TenantId": "b55f0c51-61a7-45c3-84df-33569b247796"
    }
}

We are now able to integrate Microsoft.Identity.Web with our FeatherHTTP middleware as per the code below:

Making GET and POST calls from our client apps to the API will now fail unless we present a valid token with the right scopes. If no Bearer token is present, you'll get a 401 error whereas if you have the wrong scopes, you'll get a 403

If you are using Postman, then this is how you should set up your API calls to have the right token and scopes:

Alt Text

And if we want to confirm that all is OK, i.e we have the right audience and scope, we can check our acquired token with jwt.ms

Alt Text

Adding MS Graph

To make this app a bit more realistic, I thought it would be nice to add a call to MS Graph. Again, Microsoft.Identity.Web makes it extremely straightforward to add calls to a downstream API such as MS Graph.

To interact with MS Graph, I like to use the MS Graph SDK for .NET. We could roll out our own HTTP calls but the SDK comes with a lot of goodies. Open your *.csproj file and add the Graph NuGet package:

<PackageReference Include="Microsoft.Graph.Beta" Version="0.20.0-preview" />

We then need to update our bootstrap code to add the downstream authentication like the code below:

builder.Services.AddMicrosoftWebApiAuthentication(builder.Configuration)
              .AddMicrosoftWebApiCallsWebApi(builder.Configuration)
              .AddInMemoryTokenCaches();
...
app.MapGet("/api/me", GetGraphData).RequireAuthorization();

And the API method call to MS Graph:

static async Task GetGraphData(HttpContext http)
        {
            http.VerifyUserHasAnyAcceptedScope(new string[]{"access_as_user"});

            var tokenAcquisition = http.RequestServices.GetRequiredService<ITokenAcquisition>();

            var authProvider = new DelegateAuthenticationProvider(async x => {
                var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new string[] {"User.Read"});
                x.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            });
            var graphClient = new GraphServiceClient(authProvider);
            var me = await graphClient.Me.Request().GetAsync();

            await http.Response.WriteJsonAsync(new {Name = me.GivenName, Email= me.Mail});
        }

Using Postman to make a GET call to the /api/me endpoint gives us the data we need from MS Graph

Alt Text

Summary

It was truly fun and refreshing working with the FeatherHTTP framework. I have to admit that the documentation is currently fairly "thin" and it's still early days but I have high hopes for this project. In the end, it was a great experiment to see what I can do with this early version of the framework and integrate authentication to make it more secure.

You can find the complete implementation on GitHub

Discussion (0)

pic
Editor guide