DEV Community

Cover image for Configuring .NET APIs Authorization with Keycloak
Marian Salvan
Marian Salvan

Posted on • Edited on

3 2 2 2 2

Configuring .NET APIs Authorization with Keycloak

Introduction

Keycloak is a free, open-source identity and access management solution, sponsored by Red Hat, that simplifies securing modern applications with features like Single Sign-On (SSO) and identity brokering. Its centralized authentication and authorization capabilities, along with user federation and multi-tenancy support, make it a versatile tool for managing user access across various platforms. In this article, I'll guide you through configuring a .NET API with Keycloak, all running on Docker, to create a secure and scalable environment.

For simpler application cases, exploring .NET's built-in authorization and authentication mechanisms using .NET Identity, as covered in some of my previous articles, might be sufficient and worth considering. These foundational approaches can provide the necessary security for many applications without the need for more complex solutions like Keycloak. However, as your application's requirements grow, integrating Keycloak can offer enhanced flexibility and scalability.

This article contains the following:

  1. Initial project setup
  2. Docker compose file
  3. Keycloack server configuration
  4. Securing .NET API with Keycloack

For this tutorial I am using .NET 9, Keycloak version 26.1.0 and Postgres version 17.4.

Initial project setup

For the initial setup, all you need to do is to create a regular .NET API application with the default WeatherForecastController and with the default Dockerfile. Your Dockerfileshould look something like this:

# This stage is used when running from VS in fast mode (Default for Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 80
EXPOSE 443

# This stage is used to build the service project
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["ReactApp.Server.csproj", "."]
RUN dotnet restore "./ReactApp.Server.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./ReactApp.Server.csproj" -c $BUILD_CONFIGURATION -o /app/build

# This stage is used to publish the service project to be copied to the final stage
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./ReactApp.Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ReactApp.Server.dll"]
Enter fullscreen mode Exit fullscreen mode

Docker compose file

Now that you have the project setup, you need to add the following docker compose file:

version: '3.9'

services:
  backend:
    build:  
      context: ReactApp.Server
      dockerfile: Dockerfile
    ports:
      - "5080:80"
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_HTTP_PORTS=80
    depends_on:
      - keycloak
    networks:
      - app-network

  keycloak:
    image: quay.io/keycloak/keycloak:26.1.0
    command: start-dev
    environment:
        KC_DB: postgres
        KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
        KC_DB_USERNAME: keycloak
        KC_DB_PASSWORD: password
        KC_BOOTSTRAP_ADMIN_USERNAME: admin
        KC_BOOTSTRAP_ADMIN_PASSWORD: admin
    ports:
      - "8080:8080"
    depends_on:
      - postgres
    networks:
      - app-network

  postgres:
    image: postgres:17.4
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password

    ports:
      - "5432:5432"

    volumes:
      - postgres_data_keycloack:/var/lib/postgresql/data
    networks:
      - app-network

volumes:
  postgres_data_keycloack:

networks:
  app-network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

This Docker file is self-explanatory. It has configuration for three services: the backend service on port 5080, the Keycloak service on port 8080, and the PostgreSQL database for storing Keycloak’s data on port 5432. All three services use the app-network for communication, and the database uses a volume for persistent data storage.

To run this configuration, use the following command:

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

If everything is set up correctly, you should be able to navigate to http://localhost:8080/ and see the login page for Keycloak’s management interface.

Image description

You can log in using the username and password defined in the Docker Compose file. In this case, this is adminfor both the username and password.

Keycloack server configuration

You’ll notice that the Keycloak management console offers many settings that can be adjusted based on your use case. However, to keep this tutorial concise, I will focus only on the basic configuration.

Step 1 - Create a realm

Once you log in, you need to create a realm. A realm is a security domain that manages a set of users, credentials, roles, and groups, providing isolation between different applications or services. In my case, I am using the default realm — master.

Step 2 - Create a client

In order for an application to be able to use Keycloaks's services such as authorization, authentication and many more, it first needs to be registers inside the Keycloak service. This can be done by creating a new client.

Go to Clients the click on Create client and add the following settings:

Image description

Client authentication must be enabled since this service is private. For public services, it should be disabled. In this case, use the Standard flow for authentication — the flow typically used for web applications. The other flows serve different use cases. If you're interested in the specific use cases for each flow, I've included a table with brief explanations in the appendix at the end of this article.

Image description

Since this is a backend application, there’s no need to add anything in the Login Settings section.

Securing .NET API with Keycloack

Step 1 - Add Keycloack configuration

You first need the to install the following nuget pacakge:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Enter fullscreen mode Exit fullscreen mode

In the Program.cs, add the following configuration (note that keycloak:8080 is used to identify the service, as it runs with Docker Compose):

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "http://keycloak:8080/realms/master";
        options.RequireHttpsMetadata = false; // Only for develop
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "http://localhost:8080/realms/master",
            ValidateAudience = true,
            ValidAudience = "net-web-api",
        };
);
Enter fullscreen mode Exit fullscreen mode

Don't forget to also add the proper middlewares:

app.UseAuthentication();
app.UseAuthorization();
Enter fullscreen mode Exit fullscreen mode

And finally add the [Authorize] attribute on the WeatherForecastController:

[Authorize]
[ApiController]
[Route("/api/[controller]")]
public class WeatherForecastController : ControllerBase
Enter fullscreen mode Exit fullscreen mode

Step 2 - Testing authorization

In the .NET API configuration, there is a field called ValidAudience="net-web-api", which means that only if the access token contains the audience net-web-api, it will be allowed to access the resources. To enable this, we need to create a mapper. Go to the net-api-client configuration, then navigate to Client Scopes, select the net-web-api-dedicated scope, and create the following mapper of type Audience:

Image description

Now, fetch an access token from the following endpoint so you can test the authorization:

curl --location 'http://localhost:8080/realms/master/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=net-web-api' \
--data-urlencode 'client_secret=HXduWLurrenfHLadKG71P1GbUKH2HG04' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'audience=net-web-api'
Enter fullscreen mode Exit fullscreen mode

The client_secret can be found in the Credentials tab of the net-web-api client inside the management console.

Image description

Finally, use the token obtained previously to make a call to the weatherforecast endpoint, and you should be able to get the result:

curl --location 'http://localhost:5080/api/weatherforecast' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVTDlhN25Fc3kzb0RVMGFhSVdSM3pWYkNwdEdsVnZOMjhMMFprdUJBVXVrIn0.eyJleHAiOjE3NDAyNTI2NzYsImlhdCI6MTc0MDI1MjYxNiwianRpIjoiMzIwNDg1ODAtZjlkYS00MjAzLTgyZjUtMjlhN2Y3MWVhODFlIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibmV0LXdlYi1hcGkiLCJhY2NvdW50Il0sInN1YiI6ImUzNWUyZGNmLTJmOGQtNDkxMC1hNWNkLWFiNmJmZDE1MWNhMCIsInR5cCI6IkJlYXJlciIsImF6cCI6Im5ldC13ZWItYXBpIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIvKiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1tYXN0ZXIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19LCJuZXQtd2ViLWFwaSI6eyJyb2xlcyI6WyJ1bWFfcHJvdGVjdGlvbiJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImNsaWVudEhvc3QiOiIxNzIuMTguMC4xIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtbmV0LXdlYi1hcGkiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE4LjAuMSIsImNsaWVudF9pZCI6Im5ldC13ZWItYXBpIn0.Dv0u_z25t7YRrrCBtjjM6ETbmm7HuM2oAly3RBCpNFKMvellMieimFwUHfMIiKt0Ju6JUqr8KpTfX1aekBMSkRwcBDGTgs-TMByn-mNSawbTay1WAvrwYnSPPqgk4TJolmzFooNt-zw4uHAfBmf_Lg5KAwtM6_q2vTbUJmOUbUK-KupdwfT9q9poQ_ckBcnGAz3o-xAIMcwfnmOFWzF6aINZ6ZPrD4FiFeRrzXP6JePdvfFds3O514nFt4exV1rkEDXQMDTr7fK03TYQxXOlNXwOyWJf102eMRGwBxy7SQyibB0O9bIPZWUzjuXMP2kQ6hC9T1p-PZvUTYNUSOQz1g'
Enter fullscreen mode Exit fullscreen mode

Image description

Conclusions

In this article, I have demonstrated how to configure a Keycloak server and use it to secure a .NET API. I plan to write another article where I will extend this example with a simple React application. In the following article, I will show you how to configure the React client app to integrate with the existing secure API and implement basic functionalities such as login, logout, viewing user profile data, and refresh token handling.

Appendix - Authentication flows

Flow Description Use Case
Standard Flow OAuth 2.0 Authorization Code Flow. User logs in via Keycloak, client exchanges code for tokens. Web apps with secure server-side storage.
Direct Access Grants Client sends username/password to get tokens directly. Trusted apps (e.g., CLI tools, legacy systems).
Implicit Flow Tokens are returned directly after login (no code exchange). Single-page apps (SPAs) or mobile apps (less secure, now discouraged).
Service Accounts Roles Client authenticates itself (no user) using client credentials to get tokens. Machine-to-machine (M2M) or backend services.
Device Authorization Grant User authorizes a device via a code on another device. Devices with limited input (e.g., smart TVs, IoT).
OIDC CIBA Grant Authentication happens on a separate device/channel without user interaction on the client. Scenarios where user interaction isn’t possible (e.g., banking apps, IoT).

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay