A demo api with authentication and tests
MIT License
This is a sample project that displays all the cool features I love about DOTNET. Including:
Follow along on LinkedIn for more updates. And while you're at it, let me know in the post what you think.
This project will be hosted on Azure, you can check it out here....
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.
Stephan' demo API
Accounts in any organizational directory and personal Microsoft accounts
Add
button next to Application ID URI
Add a scope
, Scope name: access_as_user
, Who can consent Admins and Users
, Pick a display name and description.Stephan' demo API client
Accounts in any organizational directory and personal Microsoft accounts
Single-page application
and enter the redirect URI https://localhost:8080/swagger/oauth2-redirect.html
(the port won't matter, for localhost)Application (client) ID
, go to the app registration from the previous step and add the client id under Expose an API
-> Authorized client applications
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).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.
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.
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.
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.
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.
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.
Want to contribute something to this demo project? Let's make it even better! Or lets discuss improvements.