demo-api-with-auth

A demo api with authentication and tests

MIT License

Stars
2

Demo API

This is a sample project that displays all the cool features I love about DOTNET. Including:

  • Minimal API
  • Token Authentication
  • OpenAPI Documentation generated at build time
  • Swagger UI Documentation (with working token authentication)
  • Tests that actual test the API test the api
  • Strongly typed API client using Kiota generated at build time
  • Open Telemetry integrated
  • Health checks
  • Aspire dashboard (standalone) integration

Follow along on LinkedIn for more updates. And while you're at it, let me know in the post what you think.

Hosted DEMO

This project will be hosted on Azure, you can check it out here....

Getting started yourself

The API is protected with a tokens from Entra ID. I got an API and a client app registered to get you started, but you'll need to do some setup yourself if you want to try this with your own app.

  1. Clone the repository
  2. Create Entra ID API registration
  3. Create Entra ID client registration
  4. Fill in the appsettings.json

Entra ID API registration

  1. Go to Entra ID app registrations and click new registration.
  2. Give your app a name Stephan' demo API
  3. Select the account types that can use your app Accounts in any organizational directory and personal Microsoft accounts
  4. Skip the redirect URI
  5. Click register
  6. Click Expose an API and click on the Add button next to Application ID URI
  7. Leave the default URI and click save (you need this value in your appsettings.json)
  8. Click on Add a scope, Scope name: access_as_user, Who can consent Admins and Users, Pick a display name and description.

Entra ID client registration

  1. Go to Entra ID app registrations and click new registration.
  2. Give your app a name Stephan' demo API client
  3. Select the account types that can use your app Accounts in any organizational directory and personal Microsoft accounts
  4. Select Single-page application and enter the redirect URI https://localhost:8080/swagger/oauth2-redirect.html (the port won't matter, for localhost)
  5. Click register
  6. Go to the API permissions and add the API you created in the previous step, select the scope you created in the previous step.
  7. Copy the Application (client) ID, go to the app registration from the previous step and add the client id under Expose an API -> Authorized client applications

Appsettings

Open the appsettings.json file in your project and fill the folling values:

  • JWT:TokenValidationParameters:ValidAudience with the Application ID URI from the API registration, and the Application id (without the api:// part). Do NOT change the order of the values!
  • Swagger:ClientId with the Application (client) ID from the client registration.
  • Swagger:Scope with the scope you created in the API registration (if you picked another name).

Testing the API

Since we now have an api that is protected with tokens from Entra ID, we need a way to get those tokens from our test project. Preferably without creating credentials in Entra ID itself, and having to manage those tokens in our test project.

This is where IdentityProxy comes in. It's a simple project that runs a webservice in a docker container which we configure to emulate tokens as if they came from Entra ID. More details can be found in this blog post.

Svrooij.Demo.Api.Tests

In the test project you'll find a MinimalApiServiceFactory this is using the WebApplicationFactory from the Microsoft.AspNetCore.Mvc.Testing package to start the API in a test environment. It also starts the IdentityProxy in a docker container, and configures the API to use the proxy as the token issuer. This class is then injected in the WeatherEndpoint.Tests class to test the API.

The factory can be used to get tokens and to create a HttpClient that is configured for the API.

You are even able to debug the actual endpoint (only the success path) by setting a breakpoint in the WeatherEndpoint class and debugging the test. The unsuccessful path is also tested, but since those responses come from the Jwt middleware, you can't debug those.

Generate OpenAPI specification

Most solutions generate the OpenAPI specification at runtime, but I prefer to generate them at compile time and to serve a static version. It won't change dynamically, but we are not using any dynamic endpoints anyway.

The generated version can be found at wwwroot/swagger/v1/swagger.json and is served by the app.UseStaticFiles() middleware.`

How does it work? Thanks to this page I got a great idea on how to make it work. In the src/Svrooij.Demo.Api project file there is a new build target that runs the SwashBuckle.AspNetCore.Cli to generate the OpenAPI specification.

There also is a target that restores the required tools after restore, making sure the tools is available when the build target runs.

And if you happen to call dotnet restore on the solution, it will also restore the tools for you, using the after.{Solution}.sln.targets file.

Kiota

The Kiota CLI is configured to generate a strongly typed client for the API. The client is generated in the src/Svrooij.Demo.Client project. It's generated in the Generated folder, using some cleaver msbuild tricks, the main ingredient is the CollectPackageReferences that is used as BeforeTargets. Meaning this target is run at the very moment the project is loaded.

The Generated folder is also added to the .gitignore file, so the client is always freshly generated. And if you want to regenerate the client, you can just delete the folder and within seconds it's back. The folder is also deleted when you call clean.

In the client project, you will also find some handy extension methods to use the Kiota generated client with dependency injection.

Observability

The API is configured to use OpenTelemetry (if you set the OTEL_EXPORTER_OTLP_ENDPOINT to the correct value). By default the exporter is disabled, but you can enable it by setting the environment variable. You can also change the endpoint to something that suits your needs.

Aspire Dashboard

If you want a quick and easy way to view the telemetry data, you can use the Aspire Dashboard, which you can run as a standalone container in docker.

The following command will run the Aspire Dashboard in a container, and exposes the OTEL endpoint at localhost:4317 and the dashboard at localhost:18888.

docker run --rm -it `
    -p 18888:18888 `
    -p 4317:18889 -d `
    --name aspire-dashboard `
    mcr.microsoft.com/dotnet/nightly/aspire-dashboard:8.1

I'm definitly not suggesting you should not look at Aspire, but if you don't want to modify your project and it's not a distributed system, you might as well just use the dashboard, and not the fully fleshed aspire distributed host.

Contributing

Want to contribute something to this demo project? Let's make it even better! Or lets discuss improvements.