Christopher Paul
March 11, 2024 11:12PM
Hi NGINX-users,

I am running nginx version: nginx/1.25.3 (nginx-plus-r31-p1 on Rocky 9.3
in a lab, trying to get OIDC authentication working to KeyCloak 23.0.7.
Attached are the relevant files /etc/nginx.conf and included
/etc/nginx/conf.d files, most of which are from the nginx-openid-connect
github repo (

Keycloak and nginx are running on the same VM.

What am I missing/doing wrong? When I try to hit the server, the
redirect to Keycloak does not happen. I can tell this for sure by
running "sudo tcpdump -i lo". There are no packets transmitted to
localhost:8080. When I "curl -v",
besides no packets between nginx and keycloak, the output of curl is:

* Connected to ( port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Unknown (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / [blank] / UNDEF
* ALPN: server accepted http/1.1
* Server certificate:
*  subject:
*  start date: Mar  7 23:46:13 2024 GMT
*  expire date: Jun  5 23:46:12 2024 GMT
*  subjectAltName: host "" matched cert's
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
*   Certificate level 0: Public key type ? (256/128 Bits/secBits),
signed using sha256WithRSAEncryption
*   Certificate level 1: Public key type ? (2048/112 Bits/secBits),
signed using sha256WithRSAEncryption
* using HTTP/1.x
> GET / HTTP/1.1
> Host:
> User-Agent: curl/8.6.0
> Accept: */*
* old SSL session ID is stale, removing
< HTTP/1.1 401 Unauthorized
< Server: nginx/1.25.3
< Date: Tue, 12 Mar 2024 03:07:32 GMT
< Content-Type: text/html
< Content-Length: 179
< Connection: keep-alive
< WWW-Authenticate: Bearer realm="closed site"
<head><title>401 Authorization Required</title></head>
<center><h1>401 Authorization Required</h1></center>
* Connection #0 to host left intact

Many thanks for any insight that might be offered on this.

Chris Paul
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log debug;
pid /var/run/;
load_module modules/;
load_module modules/;
events {
worker_connections 1024;
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
# OpenID Connect configuration
# Each map block allows multiple values so that multiple IdPs can be supported,
# the $host variable is used as the default input parameter but can be changed.
map $host $oidc_authz_endpoint {
#default ""; "https://my-idp/oauth2/v1/authorize";
default "";

map $host $oidc_authz_extra_args {
# Extra arguments to include in the request to the IdP's authorization
# endpoint.
# Some IdPs provide extended capabilities controlled by extra arguments,
# for example Keycloak can select an IdP to delegate to via the
# "kc_idp_hint" argument.
# Arguments must be expressed as query string parameters and URL-encoded
# if required.
default ""; "kc_idp_hint=another_provider"

map $host $oidc_token_endpoint {
#default "";
default "";

map $host $oidc_jwt_keyfile {
#default "";
default "";

map $host $oidc_client {
default "nginx-plus";

map $host $oidc_pkce_enable {
default 0;

map $host $oidc_client_secret {
default "UxPA37ZTMv36mTGSZhfSTFCl91YYzwcx";

map $host $oidc_scopes {
default "openid+profile+email+offline_access";

map $host $oidc_logout_redirect {
# Where to send browser after requesting /logout location. This can be
# replaced with a custom logout page, or complete URL.
default "/_logout"; # Built-in, simple logout page

map $host $oidc_hmac_key {
# This should be unique for every NGINX instance/cluster
default "f3etJkRhybOLWPAt59lWN4GmXz";

map $host $zone_sync_leeway {
# Specifies the maximum timeout for synchronizing ID tokens between cluster
# nodes when you use shared memory zone content sync. This option is only
# recommended for scenarios where cluster nodes can randomly process
# requests from user agents and there may be a situation where node "A"
# successfully received a token, and node "B" receives the next request in
# less than zone_sync_interval.
default 0; # Time in milliseconds, e.g. (zone_sync_interval * 2 * 1000)

map $proto $oidc_cookie_flags {
http "Path=/; SameSite=lax;"; # For HTTP/plaintext testing
https "Path=/; SameSite=lax; HttpOnly; Secure;"; # Production recommendation

map $http_x_forwarded_port $redirect_base {
"" $proto://$host:$server_port;
default $proto://$host:$http_x_forwarded_port;

map $http_x_forwarded_proto $proto {
"" $scheme;
default $http_x_forwarded_proto;

# Additional advanced configuration (server context) in openid_connect.server_conf

# JWK Set will be fetched from $oidc_jwks_uri and cached here - ensure writable by nginx user
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m;

# Change timeout values to at least the validity period of each token type
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
keyval_zone zone=oidc_access_tokens:1M state=conf.d/oidc_access_tokens.json timeout=1h;
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
keyval_zone zone=oidc_pkce:128K timeout=90s; # Temporary storage for PKCE code verifier.

keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for JWT
keyval $cookie_auth_token $access_token zone=oidc_access_tokens; # Exchange cookie for access token
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
keyval $request_id $new_access_token zone=oidc_access_tokens;
keyval $request_id $new_refresh zone=refresh_tokens; # ''
keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;

auth_jwt_claim_set $jwt_audience aud; # In case aud is an array
js_import oidc from conf.d/openid_connect.js;

# vim: syntax=nginx
# This is the backend application we are protecting with OpenID Connect
upstream my_backend {
zone my_backend 64k;

# Custom log format to include the 'sub' claim in the REMOTE_USER field
log_format main_jwt '$remote_addr - $jwt_claim_sub [$time_local] "$request" $status '
'$body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';

# The frontend server - reverse proxy with OpenID Connect authentication
server {
include conf.d/openid_connect.server_conf; # Authorization code flow and Relying Party processing
error_log /var/log/nginx/error.log debug; # Reduce severity level as required

listen 8010; # Use SSL/TLS in production

location / {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
error_page 401 = @do_oidc_flow;

#auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
auth_jwt_key_request /_jwks_uri; # Enable when using URL

# Successfully authenticated users are proxied to the backend,
# with 'sub' claim passed as HTTP header
proxy_set_header username $jwt_claim_sub;

# Bearer token is uses to authorize NGINX to access protected backend
#proxy_set_header Authorization "Bearer $access_token";

# Intercept and redirect "401 Unauthorized" proxied responses to nginx
# for processing with the error_page directive. Necessary if Access Token
# can expire before ID Token.
#proxy_intercept_errors on;

proxy_pass http://my_backend; # The backend site/app

access_log /var/log/nginx/access.log main_jwt;

# vim: syntax=nginx
server {
listen 80;
return 301 https://$host$request_uri;

server {
# listen 80 default_server;
listen 443 ssl;
# auth_jwt "API";
# auth_jwt_key_request "http://localhost:8080/realms/rexlab/protocol/openid-connect/certs";
# auth_jwt_type encrypted;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log debug;

ssl_certificate /etc/ssl/nginx/nginx.crt;
ssl_certificate_key /etc/ssl/nginx/nginx.key;
# ssl_verify_client on;
# ssl_trusted_certificate /etc/ssl/cachain.pem;
# ssl_ocsp on; # Enable OCSP validation

#access_log /var/log/nginx/host.access.log main;

location / {
auth_jwt_key_request /_jwks_uri;
auth_jwt "closed site" token=$cookie_auth_token;
root /usr/share/nginx/html;
index index.html index.htm;

location = /_jwks_uri {
#proxy_pass $oidc_jwt_keyfile; # Uses the mapped value
proxy_pass "";
proxy_cache_valid 200 1d; # Caches the keys for a day, adjust as needed
proxy_cache jwk; # Assumes you have a proxy_cache_path defined with `jwk` as the key
error_page 401 = @error401;

location @error401 {
proxy_pass $oidc_authz_endpoint; # Redirect to Keycloak for login
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
nginx mailing list
Subject Author Posted

missing something with auth_jwt_key_request

Christopher Paul March 11, 2024 11:12PM

Re: missing something with auth_jwt_key_request

Sergey A. Osokin March 13, 2024 04:54PM

RE: missing something with auth_jwt_key_request

Christopher Paul March 14, 2024 02:10PM

Re: missing something with auth_jwt_key_request

Sergey A. Osokin March 14, 2024 02:28PM

Sorry, only registered users may post in this forum.

Click here to login

Online Users

Guests: 319
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready