Open r300mrg opened 3 months ago
Hi @r300mrg ,
Have you tried disabling the inheritance of headers from the client request? I mean adding proxy_pass_request_headers off;
to the /_refresh
and /_token
locations in the openid_connect.server_conf
file.
Hi @r300mrg , Have you tried disabling the inheritance of headers from the client request? I mean adding
proxy_pass_request_headers off;
to the/_refresh
and/_token
locations in theopenid_connect.server_conf
file.
Hi @route443, thank you for your prompt reply. Let me try this out and get back to you. Thanks
@route443, I deployed the change you suggested (excluding proxy_set_header Access-Control-Allow-Origin *;
) and at present my testing I'm seeing mixed results.
When the token expires its either ok, or I get the same CORS error/issue, but the pages seem to load/work without issues despite the console error and site error popups due to the console log error.
Current code:
location = /_token {
# This location is called by oidcCodeExchange(). We use the proxy_ directives
# to construct the OpenID Connect token request, as per:
# http://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
internal;
proxy_ssl_server_name on; # For SNI to the IdP
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization $arg_secret_basic;
proxy_pass_request_headers off; # Added for MS Entra CORS Issue
proxy_pass $oidc_token_endpoint;
}
location = /_refresh {
# This location is called by oidcAuth() when performing a token refresh. We
# use the proxy_ directives to construct the OpenID Connect token request, as per:
# https://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken
internal;
proxy_ssl_server_name on; # For SNI to the IdP
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_header Authorization $arg_secret_basic;
proxy_pass_request_headers off; # Added for MS Entra CORS Issue
proxy_pass $oidc_token_endpoint;
}
error.log contains the following type of errors only, but these have been present before I attempted to update the code.
2024/07/29 13:40:03 [error] 744379#744379: *249 js: OIDC refresh response did not include id_token
Any other ideas/thoughts? Thanks!
@r300mrg, so, as I understand it, the current issue is that after the id_token
expires and you use the refresh token to update the tokens, you are not receiving a new id_token
(based on the error in the logs). Honestly, I don't recall such behavior from MS Entra ID, meaning that if you use the openid
scope, the IdP should issue a new id_token
. However, I'll take a look at this, maybe something has changed in its behavior.
Looking at this issue more broadly, the refresh_token
should primarily be used to update the access_token
, not the id_token
. In other words, the id_token
is used to pass the user's identity, and its lifetime should not be tied to the session duration in your web app. However, the current peculiarity of our solution is that we "exchange" the auth_token
cookie for a id_token
and validate the token on each request. So, if you don't find a solution, I'd recommend increasing the lifetime of the id_token
(for example, synchronizing it with the desired session duration of your application, like 8 hours) and disabling the refresh token, i.e., removing offline_access
from $oidc_scopes
.
Thank you @route443!
I'm not even going to pretend to be an expert (this is all new to me) :)
Effectively we are using the code here as it is, with the required environment values changed in
openid_connect_configuration.conf
with JWT file location and then locations of our sites pages which need authentication in an equivalent frontend.conf
file.
I have tried to fix the js: OIDC refresh response did not include id_token
error by adding &scope=$host $oidc_scopes
or &scope=openid
to the \_refresh
proxy_set_body, as indicated as required on https://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken, however this causes the page to completely fail loading when the token has expired and the page is reloaded.
Any help is greatly appreciated. Thank you
Hi @r300mrg ,
I checked on my side and can confirm that Entra ID reissues the id_token
when using the refresh_token
(after id_token expiration). So this part is working as expected.
I'd recommend not changing anything in openid_connect.server_conf
, especially since managing request parameters is no longer available in openid_connect.server_conf
. + try to reset $oidc_scopes
to its defaults (openid+profile+email+offline_access). If this doesn't help, I'd like to take a look at your configuration if possible, particularly openid_connect_configuration.conf
and the application manifest (App registrations -> your app -> manage -> manifest (JSON)).
Thank you in advance!
Hi @route443, my configuration is effectively the same as this main repo:
configure.sh
- as per latest main repo.
openid_connect.js
- as per latest main repo.
openid_connect.server_conf
- as per latest main repo except resolver 8.8.8.8 ipv6=off valid=30s; # For DNS lookup of IdP endpoints;
as IPv6 errors were seen before limiting to only IPv4 addresses.
openid_connect_configuration.conf
is the same except customisation on the following:
map $host $oidc_authz_endpoint { default "https://login.microsoftonline.com/<TENNANT_ID>/oauth2/authorize"; }
map $host $oidc_token_endpoint { default "https://login.microsoftonline.com/<TENNANT_ID>/oauth2/token"; }
map $host $oidc_jwt_keyfile { default "/etc/nginx/azure.jwk"; }
map $host $oidc_client { default "<key-value01>"; }
map $host $oidc_client_secret { default "<key-value02>"; }
`map $host $oidc_hmac_key {
This should be unique for every NGINX instance/cluster
default "<key-value03>";
}`
My equivalent frontend.conf
file contents are different. I've tried to re-align to some extent with proxy_set_header username $jwt_claim_sub;
or proxy_set_header Authorization "Bearer $access_token";
and even proxy_set_header Authorization "Bearer $session_jwt";
as used under other locations but these cause the site to fail loading. If I leave our current frontend.conf
equivalent file as is, but with all the other changes, when the token expires on the next site page refresh, I get a CORS error as initially posted and the error.log populates js: OIDC refresh response did not include id_token
.
Could the issue be due to the server location setup in frontend.conf?
I don't have access to the applications manifest. I can try and get the details and re-compare with the openid_connect_configuration.conf
configuration, however unfortunately I can't share this content due to security/privacy restrictions.
Hi @r300mrg ,
Could you try replacing your app (upstream) with something simpler, for example:
server {
listen 8011;
location / {
return 200 "Hello, $http_username!\n";
default_type text/plain;
}
location = /favicon.ico {
access_log off;
return 204;
}
}
and check if you get the same behavior?
@route443 sorry for the delay. Other work priorities and getting access to my dev environment are delaying my investigations/testing.
I will get back once I have an update or further questions. Thank you for your patience.
Hi @route443,
Thank you for your patience, and apologies for the long reply that follows.
I created a basic frontend.conf
as below, to test with simple html pages. This had mixed results.
# Custom log format to include the 'sub' claim in the REMOTE_USER field
log_format main_jwt_mrg '$remote_addr - $jwt_claim_sub [$time_local] "$request" $status '
'$body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main_jwt_mrg;
error_log /var/log/nginx/error.log debug; # Reduce severity level as required
server {
include conf.d/openid_connect.server_conf; # Authorization code flow and Relying Party processing
server_name ${nginx_server_name};
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/${nginx_server_name}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/${nginx_server_name}/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/${nginx_server_name}/chain.pem;
location / {
root /www/mrg-site;
try_files $uri $uri/ /index.html =404;
# 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://$server_name; # The backend site/app
}
}
# Port 80 and redirect to https URL
server {
listen 80;
server_name ${nginx_server_name};
return 301 https://$server_name$request_uri;
}
# vim: syntax=nginx
This seems to work and no visible browser errors, but also seems hit or miss. Also the error.log still contain js: OIDC refresh response did not include id_token
e.g.
2024/08/20 10:34:51 [error] 1655028#1655028: 1 js: OIDC refresh response did not include id_token 2024/08/20 10:34:52 [info] 1655028#1655028: 1 js: OIDC refresh token stored 2024/08/20 10:34:52 [info] 1655028#1655028: 1 js: OIDC success, creating session 7dc477d97050c374c1bc8e9dbf430b36 2024/08/20 10:35:42 [notice] 1655032#1655032: http file cache: /var/cache/nginx/jwk 0.000M, bsize: 4096 2024/08/20 10:35:42 [notice] 1655026#1655026: signal 17 (SIGCHLD) received from 1655032 2024/08/20 10:35:42 [notice] 1655026#1655026: cache loader process 1655032 exited with code 0 2024/08/20 10:35:42 [notice] 1655026#1655026: signal 29 (SIGIO) received 2024/08/20 10:35:51 [info] 1655027#1655027: 2 client timed out (110: Connection timed out) while waiting for request, client:
, server: 0.0.0.0:443 2024/08/20 10:35:51 [info] 1655029#1655029: 3 client timed out (110: Connection timed out) while waiting for request, client: , server: 0.0.0.0:443 2024/08/20 10:37:24 [error] 1655030#1655030: 7 js: OIDC refresh response did not include id_token 2024/08/20 10:37:27 [info] 1655030#1655030: 7 js: OIDC refresh token stored 2024/08/20 10:37:27 [info] 1655030#1655030: 7 js: OIDC success, creating session 2dcca38abfe14a38c7a6a59c596bb51a
For additional background on my environment and app setup.
We run NGINX on an Azure Virtual Machine that hosts Ubuntu 20.04. Our app and NGINX all run on this Ubuntu VM. We have an Angular frontend at /machines/ that makes calls to /api/machines express node.js server) and that we specify auth for each route etc.
Our frontend.conf
equivalent application file which creates the browser CORS issue and the js: OIDC refresh response did not include id_token
error.log message and failure to re-authorise with Microsoft SSO without a browser reload is below:
server {
include conf.d/openid_connect.server_conf; # Authorization code flow and Relying Party processing
server_name ${nginx_server_name};
location /machines {
return 301 /machines/;
}
location /machines/ {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
#auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Absent/invalid OpenID Connect token will (re)start auth process (including refresh)
error_page 401 = @do_oidc_flow;
# Successfully authenticated users are proxied to the backend
alias /www/site/apps-machines-client/;
try_files $uri $uri/ /index.html =404;
access_log /var/log/nginx/access.log main_jwt;
}
location /ping {
proxy_pass http://$server_addr:3000/ping;
}
location /graphql {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
#auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Absent/invalid OpenID Connect token will (re)start auth process (including refresh)
error_page 401 = @do_oidc_flow;
# Successfully authenticated users are proxied to the backend
proxy_pass http://$server_addr:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
proxy_set_header Access-Control-Allow-Origin *;
proxy_set_header Authorization "Bearer $session_jwt";
port_in_redirect off;
proxy_redirect http://$server_addr:3000 /;
proxy_connect_timeout 300;
#proxy_set_header username $jwt_claim_sub;
#proxy_pass http://backend; # The backend site/app
access_log /var/log/nginx/access.log main_jwt;
}
location /api/machines {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
#auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Absent/invalid OpenID Connect token will (re)start auth process (including refresh)
error_page 401 = @do_oidc_flow;
# Successfully authenticated users are proxied to the backend
rewrite ^/api/machines(/.*)$ $1 break; # Strip '/api/machines' prefix before proxying
proxy_pass http://$server_addr:7000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
proxy_set_header Access-Control-Allow-Origin *;
proxy_set_header Authorization "Bearer $session_jwt";
proxy_pass_header Authorization;
port_in_redirect off;
proxy_redirect http://$server_addr:7000 /;
proxy_connect_timeout 300;
#proxy_set_header username $jwt_claim_sub;
#proxy_pass http://backend; # The backend site/app
access_log /var/log/nginx/access.log main_jwt;
}
location /api/machines/sse {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
#auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Absent/invalid OpenID Connect token will (re)start auth process (including refresh)
error_page 401 = @do_oidc_flow;
# Successfully authenticated users are proxied to the backend
rewrite ^/api/machines(/.*)$ $1 break; # Strip '/api/machines' prefix before proxying
proxy_pass http://$server_addr:7000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
proxy_set_header Access-Control-Allow-Origin *;
proxy_set_header Authorization "Bearer $session_jwt";
# Required headers for SSE
proxy_set_header X-Accel-Buffering "no";
proxy_buffering off;
# Additional configuration for improved handling of connections
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
proxy_pass_header Authorization;
port_in_redirect off;
proxy_redirect http://$server_addr:7000 /;
proxy_connect_timeout 300;
access_log /var/log/nginx/access.log main_jwt;
}
location /api/machines/ping {
proxy_pass http://$server_addr:7000/ping;
}
location /api/user {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
#auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Absent/invalid OpenID Connect token will (re)start auth process (including refresh)
error_page 401 = @do_oidc_flow;
# Successfully authenticated users are proxied to the backend
proxy_pass http://$server_addr:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
proxy_set_header Access-Control-Allow-Origin *;
proxy_set_header Authorization "Bearer $session_jwt";
port_in_redirect off;
proxy_redirect http://$server_addr:3000 /api/user;
proxy_connect_timeout 300;
#proxy_set_header username $jwt_claim_sub;
#proxy_pass http://backend; # The backend site/app
access_log /var/log/nginx/access.log main_jwt;
}
location / {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
#auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Absent/invalid OpenID Connect token will (re)start auth process (including refresh)
error_page 401 = @do_oidc_flow;
# Successfully authenticated users are proxied to the backend
root /www/site/apps-machines-client;
index index.html index.htm;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/${nginx_server_name}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${nginx_server_name}/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/${nginx_server_name}/chain.pem;
}
server {
if ($host = ${nginx_server_name}) {
return 301 https://$host$request_uri;
}
listen 80 default_server;
server_name ${nginx_server_name};
return 404;
}
One difference in our frontend.conf
file to the current main branch is:
#Existing
proxy_set_header Authorization "Bearer $session_jwt";
#New
# Bearer token is uses to authorize NGINX to access protected backend
#proxy_set_header Authorization "Bearer $access_token";
Our app has been built on a legacy openid-connect "Bearer $session_jwt"
header passed and not the current Bearer $access_token
, but even with the older existing openid-connect config files the site works until the authorisation token expires and the SSO refresh fails.
Any thoughts or recommendations?
Many thanks
Hi @route443,
Just an update on our investigations and updates from my side.
The error log entry js: OIDC refresh response did not include id_token
looks to have been fixed.
Our configuration was still using the the v1.0 token Microsoft Entra ID Oauth links for $host $oidc_authz_endpoint
and $oidc_token_endpoint
.
e.g. https://login.microsoftonline.com/{tennant-id}/oauth2/authorize
and not https://login.microsoftonline.com/{tennant-id}/oauth2/v2.0/authorize
and our Microsoft App registrations settings had conflicting "accessTokenAcceptedVersion": 2,
.
We still have the CORS initial browser error, but are looking to re-align the "Bearer $session_jwt"
and Bearer $access_token
settings in our application.
Using the latest code as of July 26th 2024 and configured to use Identity Provider (IdP) of Microsoft Entra ID (updating our dev server running legacy version of this code from a few years ago, not ideal but coming from a working setup at one point in time).
Initial site loads and authenticates as expected. However, after the authentication token expires the browser refresh fails to re-authenticate with the browser console providing the following CORS related issue:
I’ve attempted to add
proxy_set_header Access-Control-Allow-Origin *;
under both/_token
and/_refresh
in fileopenid_connect.server_conf
, and this makes no difference.Any thoughts or ideas how to resolve? Thanks