[GH-ISSUE #15107] issue: we using the custom auth logic for auto login by passing the trusted header feature using the nginx proxy pass #33001

Closed
opened 2026-04-25 06:51:11 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @priyadharsan1403 on GitHub (Jun 18, 2025).
Original GitHub issue: https://github.com/open-webui/open-webui/issues/15107

Check Existing Issues

  • I have searched the existing issues and discussions.
  • I am using the latest version of Open WebUI.

Installation Method

Git Clone

Open WebUI Version

v0.3.11

Ollama Version (if applicable)

No response

Operating System

ubuntu 22.04

Browser (if applicable)

chrome

Confirmation

  • I have read and followed all instructions in README.md.
  • I am using the latest version of both Open WebUI and Ollama.
  • I have included the browser console logs.
  • I have included the Docker container logs.
  • I have provided every relevant configuration, setting, and environment variable used in my setup.
  • I have clearly listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc).
  • I have documented step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation. My steps:
  • Start with the initial platform/version/OS and dependencies used,
  • Specify exact install/launch/configure commands,
  • List URLs visited, user input (incl. example values/emails/passwords if needed),
  • Describe all options and toggles enabled or changed,
  • Include any files or environmental changes,
  • Identify the expected and actual result at each stage,
  • Ensure any reasonably skilled user can follow and hit the same issue.

Expected Behavior

to header should pass to open webui post where it running 3001

Actual Behavior

my nginx script :
server {
listen 80;
listen [::]:80;
server_name subdomain.domain.com;

add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;

location /nexus_chat {
# Authentication
auth_request /auth-verify;

auth_request_set $user_id $upstream_http_x_user_id;
auth_request_set $user_email $upstream_http_x_user_email;
auth_request_set $user_name $upstream_http_x_user_name;
auth_request_set $api_key $upstream_http_x_api_key;
auth_request_set $user_org $upstream_http_x_user_org;
 # REQUIRED: Auto-login headers (must match env vars above)
proxy_set_header X-User-Email $user_email;
proxy_set_header X-User-Name $user_name;

# OPTIONAL: Additional context headers
proxy_set_header X-User-ID $user_id;
proxy_set_header X-User-Org $user_org;
proxy_set_header X-API-Key $api_key;

# Forward to WebUI with user context headers
proxy_pass http://127.0.0.1:3001;



# Standard proxy headers
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 X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# SSE support
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
add_header X-Debug-User-Email $user_email always;
add_header X-Debug-User-Name $user_name always;
add_header X-Debug-User-ID $user_id always;

}
# Auth verification endpoint (internal)
location = /auth-verify {
internal;
proxy_pass http://127.0.0.1:5000/verify$is_args$args;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Authorization $http_authorization;
add_header X-Debug-User-ID $upstream_http_x_user_id always;
add_header X-Debug-Email $upstream_http_x_user_email always;
# TEMP: Log something to be sure it's hit
access_log /var/log/nginx/verify_access.log;
}
# Catch-all for any other /nexus_chat/* paths (rewrite to root for Open WebUI)
location / {
#rewrite ^/nexus_chat/(.*) /$1 break;
proxy_pass http://127.0.0.1:3001;
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 X-Forwarded-Proto $scheme;
proxy_set_header X-User-Email $user_email;
proxy_set_header X-User-Name $user_name;
proxy_set_header X-User-ID $user_id;
proxy_set_header X-User-Org $user_org;
proxy_set_header X-API-Key $api_key;
}
}

my authmiddle script

const express = require('express');
const jwt = require('jsonwebtoken');
const { Pool } = require('pg');
const axios = require('axios');

const app = express();

const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false, // Only use this in development
},
});

app.get('/verify', async (req, res) => {
try {
console.log('req.query:', req.query);
console.log('req.headers:', req.headers);
console.log('req.body:', req.body);

let token = req.query.token;

if (!token && req.headers['x-original-uri']) {
  try {
    const rawUri = req.headers['x-original-uri']; // e.g. "/nexus_chat?token=abcd..."
    const urlObj = new URL(`http://dummy${rawUri}`);
    token = urlObj.searchParams.get('token');
    console.log('Extracted token from x-original-uri:', token);
  } catch (err) {
    console.error('Failed to parse token from x-original-uri:', err.message);
  }
}

if (!token) {
  return res.status(401).send('No token provided');
}



const decoded = jwt.verify(token, process.env.JWT_SECRET || "99dd4c9a7e2b5336754022d1a3a1cbe971008eed7a66d4ff405c0fd1ed6cebb87e37507e6a71310449b0602cba314eb17c6b625e9d3e9a17d75e1fcfb03d952b");

console.log('Decoded token:', decoded);

if (!decoded || !decoded.userId || !decoded.orgId) {
  return res.status(401).send('Invalid token structure');
}

const { userId, orgId } = decoded;

const userResult = await pool.query(
  `SELECT id, email_address, first_name, lite_llm_key FROM users WHERE id = $1`,
  [userId]
);

console.log('User query result:', userResult.rows);

if (userResult.rows.length === 0) {
  return res.status(401).send('User not found or inactive');
}

const user = userResult.rows[0];

//await ensureWebUIUser(user, userId, orgId);

res.setHeader('X-User-ID', userId);
res.setHeader('X-User-Email', user.email_address);
res.setHeader('X-User-Org', orgId);
res.setHeader('X-User-Name', user.first_name);
res.setHeader('X-API-Key', user.lite_llm_key);

console.log('User headers set:', {
  'X-User-ID': userId,
  'X-User-Email': user.email_address,
  'X-User-Org': orgId,
  'X-User-Name': user.first_name,
  'X-API-Key': user.lite_llm_key,
});

return res.sendStatus(200);

} catch (error) {
console.error('Auth verify error:', error);
res.status(401).send('Invalid token');
}
});

async function ensureWebUIUser(user, userId, orgId) {
const webuiUserId = user_${userId}_org_${orgId};
const base = 'http://open-webui:8080/api/v1';
const headers = {
Authorization: Bearer ${process.env.WEBUI_API_KEY}
};

try {
// Check if user exists
await axios.get(${base}/users/${webuiUserId}, { headers });

// Update user info
await axios.put(`${base}/users/${webuiUserId}`, {
  email: user.email_address,
  name: user.first_name,
  role: 'user',
}, { headers });

console.log(`✅ Updated existing WebUI user: ${webuiUserId}`);
return;

} catch (err) {
const status = err.response?.status;
console.log(User lookup returned status ${status});
if (status !== 404) {
console.error('Error checking user:', err.message);
return;
}

// If not found, sign up
try {
  await axios.post(`${base}/auths/signup`, {
    email: user.email_address,
    name: user.first_name,
    password: "nexuschat123456", // Ideally from env or user-provided
    profile_image_url: null,
  });

  console.log(`✅ Created WebUI user: ${webuiUserId}`);
} catch (signupErr) {
  console.error('❌ Signup failed:', signupErr.response?.data || signupErr.message);
}

}
}

app.listen(3000, () => {
console.log('Auth middleware server running on port 3000');
});

docker setup :

open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: nexus-chat-webui
ports:
- "3001:8080"
environment:

    # CRITICAL: Disable WebUI's built-in authentication
  - WEBUI_AUTH=false
  - WEBUI_AUTH_TYPE=trusted-header
  - WEBUI_AUTH_TRUSTED_HEADER=true
  
  # CRITICAL: Enable trusted header authentication
  #- ENABLE_OAUTH_SIGNUP=false
  - ENABLE_LOGIN_FORM=false
  - ENABLE_SIGNUP=false
  
  # CRITICAL: Set trusted headers (EXACT header names from Nginx)
  - WEBUI_AUTH_TRUSTED_EMAIL_HEADER=X-User-Email
  - WEBUI_AUTH_TRUSTED_NAME_HEADER=X-User-Name
  
  # ADDITIONAL: Trust all headers from reverse proxy
  - WEBUI_AUTH_TRUSTED_HEADER_AUTHENTICATION=true
  - WEBUI_URL=https://chatdev.navigatelabsai.com
  
  # Set default user role for auto-created users
  #- DEFAULT_USER_ROLE=user
  # Optional: Additional user context headers
  - WEBUI_SECRET_KEY="my  key"
  - WEBUI_NAME="demo chat"
  #- DEFAULT_USER_ROLE=user

  # LiteLLM integration
  - OPENAI_API_BASE_URL=http://litellm:8000/v1

Steps to Reproduce

  1. create the chat sesstion token usign the jwt secert
    like this subdomain.domain.com?token=hjsdjhkadjhjkj
  2. nginx will redirect to authmiddle to fech the user details in db
  3. pass the header to open web ui for trused header

here is the isssue unable to pass the header

Logs & Screenshots

{
"detail": "Your provider has not provided a trusted header. Please contact your administrator for assistance."
}

Additional Information

No response

Originally created by @priyadharsan1403 on GitHub (Jun 18, 2025). Original GitHub issue: https://github.com/open-webui/open-webui/issues/15107 ### Check Existing Issues - [x] I have searched the existing issues and discussions. - [x] I am using the latest version of Open WebUI. ### Installation Method Git Clone ### Open WebUI Version v0.3.11 ### Ollama Version (if applicable) _No response_ ### Operating System ubuntu 22.04 ### Browser (if applicable) chrome ### Confirmation - [x] I have read and followed all instructions in `README.md`. - [x] I am using the latest version of **both** Open WebUI and Ollama. - [x] I have included the browser console logs. - [x] I have included the Docker container logs. - [x] I have **provided every relevant configuration, setting, and environment variable used in my setup.** - [x] I have clearly **listed every relevant configuration, custom setting, environment variable, and command-line option that influences my setup** (such as Docker Compose overrides, .env values, browser settings, authentication configurations, etc). - [x] I have documented **step-by-step reproduction instructions that are precise, sequential, and leave nothing to interpretation**. My steps: - Start with the initial platform/version/OS and dependencies used, - Specify exact install/launch/configure commands, - List URLs visited, user input (incl. example values/emails/passwords if needed), - Describe all options and toggles enabled or changed, - Include any files or environmental changes, - Identify the expected and actual result at each stage, - Ensure any reasonably skilled user can follow and hit the same issue. ### Expected Behavior to header should pass to open webui post where it running 3001 ### Actual Behavior my nginx script : server { listen 80; listen [::]:80; server_name subdomain.domain.com; add_header X-Content-Type-Options nosniff always; add_header X-XSS-Protection "1; mode=block" always; location /nexus_chat { # Authentication auth_request /auth-verify; auth_request_set $user_id $upstream_http_x_user_id; auth_request_set $user_email $upstream_http_x_user_email; auth_request_set $user_name $upstream_http_x_user_name; auth_request_set $api_key $upstream_http_x_api_key; auth_request_set $user_org $upstream_http_x_user_org; # REQUIRED: Auto-login headers (must match env vars above) proxy_set_header X-User-Email $user_email; proxy_set_header X-User-Name $user_name; # OPTIONAL: Additional context headers proxy_set_header X-User-ID $user_id; proxy_set_header X-User-Org $user_org; proxy_set_header X-API-Key $api_key; # Forward to WebUI with user context headers proxy_pass http://127.0.0.1:3001; # Standard proxy headers 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 X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # SSE support proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; add_header X-Debug-User-Email $user_email always; add_header X-Debug-User-Name $user_name always; add_header X-Debug-User-ID $user_id always; } # Auth verification endpoint (internal) location = /auth-verify { internal; proxy_pass http://127.0.0.1:5000/verify$is_args$args; proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri; proxy_set_header Authorization $http_authorization; add_header X-Debug-User-ID $upstream_http_x_user_id always; add_header X-Debug-Email $upstream_http_x_user_email always; # TEMP: Log something to be sure it's hit access_log /var/log/nginx/verify_access.log; } # Catch-all for any other /nexus_chat/* paths (rewrite to root for Open WebUI) location / { #rewrite ^/nexus_chat/(.*) /$1 break; proxy_pass http://127.0.0.1:3001; 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 X-Forwarded-Proto $scheme; proxy_set_header X-User-Email $user_email; proxy_set_header X-User-Name $user_name; proxy_set_header X-User-ID $user_id; proxy_set_header X-User-Org $user_org; proxy_set_header X-API-Key $api_key; } } my authmiddle script const express = require('express'); const jwt = require('jsonwebtoken'); const { Pool } = require('pg'); const axios = require('axios'); const app = express(); const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: { rejectUnauthorized: false, // Only use this in development }, }); app.get('/verify', async (req, res) => { try { console.log('req.query:', req.query); console.log('req.headers:', req.headers); console.log('req.body:', req.body); let token = req.query.token; if (!token && req.headers['x-original-uri']) { try { const rawUri = req.headers['x-original-uri']; // e.g. "/nexus_chat?token=abcd..." const urlObj = new URL(`http://dummy${rawUri}`); token = urlObj.searchParams.get('token'); console.log('Extracted token from x-original-uri:', token); } catch (err) { console.error('Failed to parse token from x-original-uri:', err.message); } } if (!token) { return res.status(401).send('No token provided'); } const decoded = jwt.verify(token, process.env.JWT_SECRET || "99dd4c9a7e2b5336754022d1a3a1cbe971008eed7a66d4ff405c0fd1ed6cebb87e37507e6a71310449b0602cba314eb17c6b625e9d3e9a17d75e1fcfb03d952b"); console.log('Decoded token:', decoded); if (!decoded || !decoded.userId || !decoded.orgId) { return res.status(401).send('Invalid token structure'); } const { userId, orgId } = decoded; const userResult = await pool.query( `SELECT id, email_address, first_name, lite_llm_key FROM users WHERE id = $1`, [userId] ); console.log('User query result:', userResult.rows); if (userResult.rows.length === 0) { return res.status(401).send('User not found or inactive'); } const user = userResult.rows[0]; //await ensureWebUIUser(user, userId, orgId); res.setHeader('X-User-ID', userId); res.setHeader('X-User-Email', user.email_address); res.setHeader('X-User-Org', orgId); res.setHeader('X-User-Name', user.first_name); res.setHeader('X-API-Key', user.lite_llm_key); console.log('User headers set:', { 'X-User-ID': userId, 'X-User-Email': user.email_address, 'X-User-Org': orgId, 'X-User-Name': user.first_name, 'X-API-Key': user.lite_llm_key, }); return res.sendStatus(200); } catch (error) { console.error('Auth verify error:', error); res.status(401).send('Invalid token'); } }); async function ensureWebUIUser(user, userId, orgId) { const webuiUserId = `user_${userId}_org_${orgId}`; const base = 'http://open-webui:8080/api/v1'; const headers = { Authorization: `Bearer ${process.env.WEBUI_API_KEY}` }; try { // Check if user exists await axios.get(`${base}/users/${webuiUserId}`, { headers }); // Update user info await axios.put(`${base}/users/${webuiUserId}`, { email: user.email_address, name: user.first_name, role: 'user', }, { headers }); console.log(`✅ Updated existing WebUI user: ${webuiUserId}`); return; } catch (err) { const status = err.response?.status; console.log(`User lookup returned status ${status}`); if (status !== 404) { console.error('Error checking user:', err.message); return; } // If not found, sign up try { await axios.post(`${base}/auths/signup`, { email: user.email_address, name: user.first_name, password: "nexuschat123456", // Ideally from env or user-provided profile_image_url: null, }); console.log(`✅ Created WebUI user: ${webuiUserId}`); } catch (signupErr) { console.error('❌ Signup failed:', signupErr.response?.data || signupErr.message); } } } app.listen(3000, () => { console.log('Auth middleware server running on port 3000'); }); docker setup : open-webui: image: ghcr.io/open-webui/open-webui:main container_name: nexus-chat-webui ports: - "3001:8080" environment: # CRITICAL: Disable WebUI's built-in authentication - WEBUI_AUTH=false - WEBUI_AUTH_TYPE=trusted-header - WEBUI_AUTH_TRUSTED_HEADER=true # CRITICAL: Enable trusted header authentication #- ENABLE_OAUTH_SIGNUP=false - ENABLE_LOGIN_FORM=false - ENABLE_SIGNUP=false # CRITICAL: Set trusted headers (EXACT header names from Nginx) - WEBUI_AUTH_TRUSTED_EMAIL_HEADER=X-User-Email - WEBUI_AUTH_TRUSTED_NAME_HEADER=X-User-Name # ADDITIONAL: Trust all headers from reverse proxy - WEBUI_AUTH_TRUSTED_HEADER_AUTHENTICATION=true - WEBUI_URL=https://chatdev.navigatelabsai.com # Set default user role for auto-created users #- DEFAULT_USER_ROLE=user # Optional: Additional user context headers - WEBUI_SECRET_KEY="my key" - WEBUI_NAME="demo chat" #- DEFAULT_USER_ROLE=user # LiteLLM integration - OPENAI_API_BASE_URL=http://litellm:8000/v1 ### Steps to Reproduce 1. create the chat sesstion token usign the jwt secert like this subdomain.domain.com?token=hjsdjhkadjhjkj 2. nginx will redirect to authmiddle to fech the user details in db 3. pass the header to open web ui for trused header here is the isssue unable to pass the header ### Logs & Screenshots { "detail": "Your provider has not provided a trusted header. Please contact your administrator for assistance." } ### Additional Information _No response_
GiteaMirror added the bug label 2026-04-25 06:51:11 -05:00
Author
Owner

@tjbck commented on GitHub (Jun 18, 2025):

Unable to reproduce, keep us updated.

<!-- gh-comment-id:2983536568 --> @tjbck commented on GitHub (Jun 18, 2025): Unable to reproduce, keep us updated.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#33001