X509 with Nginx and Azure Virtual Desktop

Hello all! Hopefully someone here can help me with my current issue. I have a Keycloak instance inside of a rootless Docker container handling my authentication to Mattermost instance. The server has Nginx to handle the reverse proxy to Keycloak.

For my specific use case, I was able to configure x509 cards as a means to authenticate to Keycloak in order log into Mattermost. However, the problem I am having now is that my organization uses a VDI solution, specifically Azure Virtual Desktop (AVD) that requires a user to authenticate to the browser before accessing the internet with x509. When a user navigates to the Keycloak URL they are not prompted for their x509.

However, when not using the AVD and accessing the URL from any other means it prompts for the x509 card just fine. I have checked the browser flow and that doesn’t seem to be the issue, but am not sure how to fix this problem or where to start. Any help would be appreciated.

Ngnix conf:

server {
        listen 80;
        server_name ******.******* ******.*******;
        return 301 https://$server_name$request_uri;
}


server {
        listen 443 ssl http2;
        ssl_certificate /etc/nginx/******.*******/******.*******/tls.crt;
        ssl_certificate_key /etc/nginx/******.*******/******.*******/tls.key;
        server_name ******.******* ******.*******;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384';
        ssl_prefer_server_ciphers on;

        ssl_client_certificate /etc/nginx/******.*******/******.*******/nginx_ssl_bundle.pem;
        ssl_verify_depth 4;
        ssl_verify_client optional;

        location / {
            proxy_pass https://*.*.*.*:*****;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header SSL_CLIENT_CERT $ssl_client_escaped_cert;

            proxy_buffer_size 64k;
            proxy_buffers 8 64k;
        }
}

Docker compose

services:
  keycloak_web:
    image: quay.io/keycloak/keycloak:26.1.2
    container_name: keycloak
    env_file: .env
    ports:
      - "*****:8443"
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://keycloakdb:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: ********************
      KC_HOSTNAME: ********************
      KC_HOSTNAME_STRICT: true
      KC_HOSTNAME_STRICT_HTTPS: true
      KC_HTTP_ENABLED: true
      KC_HTTPS_CERTIFICATE_FILE: /opt/tls/tls.crt
      KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/tls/tls.key
      KC_HTTPS_CLIENT_AUTH: request
      KC_HTTPS_TRUST_STORE_FILE: /opt/x509/keycloak.truststore
      KC_HTTPS_TRUST_STORE_PASSWORD: ********************
      KC_HTTPS_KEY_STORE_TYPE: JKS
      PROXY_ADDRESS_FORWARDING: true
      KC_SPI_X509CERT_LOOKUP_PROVIDER: nginx
      KC_SPI_X509CERT_LOOKUP_NGINX_SSL_CLIENT_CERT: SSL_CLIENT_CERT
      KC_TRANSACTION_XA_ENABLED: false
      KC_HTTP_RELATIVE_PATH: /auth
      KC_METRICS_ENABLED: true
      KC_HEALTH_ENABLED: true
    volumes:
      - /********/********/********/certs:/opt/tls
      - /********/********/********/keycloak.truststore:/opt/x509/keycloak.truststore
    command:
      - start
      - --spi-x509cert-lookup-provider=nginx
      - --spi-x509cert-lookup-nginx-certificate-header=SSL_CLIENT_CERT
      - --spi-x509cert-lookup-nginx-enable=true
    depends_on:
      keycloakdb:
        condition: service_healthy
    restart: always
    healthcheck:
      test: ["CMD", "curl", "--silent", "--fail", "https://localhost:8443/auth/health/ready"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  keycloakdb:
    image: postgres:15.2
    container_name: keycloakdb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: ********************
      POSTGRES_USER: ********************
      POSTGRES_PASSWORD: ********************
      POSTGRES_HOST_AUTH_METHOD: scram-sha-256
      POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256
    restart: always
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "keycloak"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

volumes:
  postgres_data:
    driver: local