diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index 1e96689d7e..2f6b4ed632 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -813,6 +813,17 @@ FORWARD_USER_INFO_HEADER_USER_ROLE = os.getenv('FORWARD_USER_INFO_HEADER_USER_RO FORWARD_SESSION_INFO_HEADER_MESSAGE_ID = os.getenv('FORWARD_SESSION_INFO_HEADER_MESSAGE_ID', 'X-OpenWebUI-Message-Id') FORWARD_SESSION_INFO_HEADER_CHAT_ID = os.getenv('FORWARD_SESSION_INFO_HEADER_CHAT_ID', 'X-OpenWebUI-Chat-Id') +# If set while ENABLE_FORWARD_USER_INFO_HEADERS is True, send one signed HS256 JWT +# (FORWARD_USER_INFO_HEADER_JWT) instead of separate X-OpenWebUI-User-* headers. +FORWARD_USER_INFO_HEADER_JWT_SECRET = (os.environ.get('FORWARD_USER_INFO_HEADER_JWT_SECRET') or '').strip() or None +FORWARD_USER_INFO_HEADER_JWT = os.environ.get('FORWARD_USER_INFO_HEADER_JWT', 'X-OpenWebUI-User-Jwt') +try: + FORWARD_USER_INFO_HEADER_JWT_EXPIRES_SECONDS = int( + os.environ.get('FORWARD_USER_INFO_HEADER_JWT_EXPIRES_SECONDS', '300') + ) +except ValueError: + FORWARD_USER_INFO_HEADER_JWT_EXPIRES_SECONDS = 300 + #################################### # Progressive Web App #################################### diff --git a/backend/open_webui/utils/headers.py b/backend/open_webui/utils/headers.py index 7c6751f7f3..72a5d4c4d1 100644 --- a/backend/open_webui/utils/headers.py +++ b/backend/open_webui/utils/headers.py @@ -1,14 +1,55 @@ +import logging +import time +from typing import Any, Optional from urllib.parse import quote +import jwt from open_webui.env import ( + FORWARD_USER_INFO_HEADER_JWT, + FORWARD_USER_INFO_HEADER_JWT_EXPIRES_SECONDS, + FORWARD_USER_INFO_HEADER_JWT_SECRET, FORWARD_USER_INFO_HEADER_USER_EMAIL, FORWARD_USER_INFO_HEADER_USER_ID, FORWARD_USER_INFO_HEADER_USER_NAME, FORWARD_USER_INFO_HEADER_USER_ROLE, ) +log = logging.getLogger(__name__) + + +def _mint_forward_user_jwt(user: Any) -> str: + now = int(time.time()) + payload = { + 'sub': str(user.id), + 'email': str(user.email), + 'name': str(user.name), + 'role': str(user.role), + 'iss': 'open-webui', + 'iat': now, + 'exp': now + FORWARD_USER_INFO_HEADER_JWT_EXPIRES_SECONDS, + } + return jwt.encode(payload, FORWARD_USER_INFO_HEADER_JWT_SECRET, algorithm='HS256') + + +def include_user_info_headers(headers: dict, user: Optional[Any] = None) -> dict: + """ + Forward user identity to external backends: signed JWT in + FORWARD_USER_INFO_HEADER_JWT if FORWARD_USER_INFO_HEADER_JWT_SECRET is set; + otherwise the legacy X-OpenWebUI-User-* headers. + """ + if user is None: + return headers + + if FORWARD_USER_INFO_HEADER_JWT_SECRET: + try: + token = _mint_forward_user_jwt(user) + return {**headers, FORWARD_USER_INFO_HEADER_JWT: token} + except Exception: + log.exception( + 'Failed to mint %s; falling back to plain user-info headers.', + FORWARD_USER_INFO_HEADER_JWT, + ) -def include_user_info_headers(headers, user): return { **headers, FORWARD_USER_INFO_HEADER_USER_NAME: quote(user.name, safe=' '),