skip to Main Content

Hey I am trying to set up .NET api authentication with use of keycloak. Authentication works fine when API is not inside of the container but when I run it with use of docker-compose the result is always 401. I am quite new to docker so I used the docker-compose and docker file creation with use of VisualStudio and then I tried to do it myself, but in both cases request always returned 401.

API is created with .NET 6
Docker version is 20.10.22
Keycloak version is 20.0.3

Here are some images of my basic setup:

Program.cs

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Test.OrderApi.Settings;

var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();       
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
            {
                o.Authority = "http://host.docker.internal:8080/realms/test";
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false
                };

                o.RequireHttpsMetadata = false;
            });

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Dockerfile for the API

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["tests/TestDummyMicroservices/Test.Order.Api/Test.OrderApi.csproj", "tests/TestDummyMicroservices/Test.Order.Api/"]
RUN dotnet restore "tests/TestDummyMicroservices/Test.Order.Api/Test.OrderApi.csproj"
COPY . .
WORKDIR "/src/tests/TestDummyMicroservices/Test.Order.Api"
RUN dotnet build "Test.OrderApi.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Test.OrderApi.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Test.OrderApi.dll"]

docker-compose

version: '3.4'

services:
  test.orderapi:
    container_name: order-api
    image: ${DOCKER_REGISTRY-}testorderapi
    build:
      context: .
      dockerfile: tests/TestDummyMicroservices/Test.Order.Api/Dockerfile
    networks:
      - keycloak_network
   
  postgres_keycloak:
    container_name: postgres-keycloak
    image: postgres:14.6
    command: postgres -c 'max_connections=200'
    volumes:
      - pgdata_keycloak:/var/lib/postgressql/data
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    healthcheck:
      test: "exit 0"
    ports:
      - "5436:5432"
    networks:
      - keycloak_network

  keycloak:
    container_name: keycloak
    image: quay.io/keycloak/keycloak:20.0.3
    command: start-dev
    environment: 
        KC_DB: postgres
        KC_DB_URL_HOST: postgres_keycloak
        KC_DB_URL_DATABASE: keycloak
        KC_DB_PASSWORD: password
        KC_DB_USERNAME: keycloak
        KC_DB_SCHEMA: public
        KEYCLOAK_ADMIN: admin
        KEYCLOAK_ADMIN_PASSWORD: admin
        KEYCLOAK_FRONTEND_URL: test.orderapi
    links:
      - test.orderapi
    depends_on:
        postgres_keycloak:
          condition: service_healthy
    ports:
        - "8080:8080"
    networks:
      - keycloak_network

volumes:
  pgdata_keycloak:
networks:
  keycloak_network:
    driver: bridge

docker-compose.override


version: '3.4'

services:
  test.orderapi:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "80"

  test.gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "80"

How it looks inside of docker

Result of sending request

If you need any more information please let me know

I try to authenticate .NET web API with use of Keycloak

2

Answers


  1. Chosen as BEST ANSWER

    Ok I figured this one out you have to add ValidIssuers where you specify host.docker.internal and localhost so then authentication can see them

    So I added KeyckloakSettings class which takes configuration:

    namespace SMS.OrderApi.Settings
    {
        public class KeycloakSettings
        {
            public string DockerRelamUrl { get; }
            public string AppRelamUrl { get; }
    
            public KeycloakSettings(IConfiguration config)
            {
                DockerRelamUrl = config.GetValue<string>("KeyCloak:DockerRelamUrl");
                AppRelamUrl = config.GetValue<string>("KeyCloak:AppRelamUrl");
            }
        }
    }
    

    Added settings to the config

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*",
      "KeyCloak": {
        "DockerRelamUrl": "http://host.docker.internal:8080/realms/sms",
        "AppRelamUrl": "http://localhost:8080/realms/sms"
      }
    }
    

    Then added new array of TokenValidationParameters.ValidIssuers to the JwtBearer in Program.cs

    var builder = WebApplication.CreateBuilder(args);
    var config = builder.Configuration;
    
    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    
    var _keycloakSettings = new KeycloakSettings(config);
    
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
                {
                    o.Authority = _keycloakSettings.DockerRelamUrl;
                    o.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateAudience = false
                    };
    
                    o.RequireHttpsMetadata = false;
    
                    o.TokenValidationParameters.ValidIssuers = new[]
                    {
                        _keycloakSettings.DockerRelamUrl,
                        _keycloakSettings.AppRelamUrl,
                    };
                });
    
    var app = builder.Build();
    
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    
    app.UseHttpsRedirection();
    
    app.UseAuthentication();
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

  2. Your application runs on posts 5201 (http) and 7201 (https).

    In your docker compose file you need to forward the proper ports for http/https

        ports:
          - "8001:5201"
          - "8002:7201"
    

    And also set keycloack in the same port in docker or adapt your connection strings to use new port (5436)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search