Overview
OpenRAG already passes a universal JWT as the Authorization header when making OpenSearch calls on SaaS. However, Docling Serve authentication is currently gated exclusively behind the IBM_AUTH_ENABLED flag (introduced in #1551 and #1545 by @lucaseduoli). Non-IBM SaaS deployments therefore send Docling requests with no Authorization header.
This issue tracks bringing Docling Serve auth to parity with OpenSearch by propagating the universal JWT on every Docling call, regardless of deployment type.
Related to #1717.
Problem
DoclingService._get_auth_headers() (src/services/docling_service.py)
def _get_auth_headers(self, user_id=None, auth_header=None):
headers = {}
if IBM_AUTH_ENABLED: # <-- JWT only injected for IBM deployments
if auth_header:
headers["Authorization"] = auth_header
if user_id:
headers["X-Tenant-Id"] = user_id
return headers
Methods that bypass _get_auth_headers() entirely
check_task_status() — status polling uses the default client with no auth headers
fetch_task_result() — result retrieval uses the default client with no auth headers
flows/components/docling_remote.py (Langflow component)
- Exposes a generic
api_headers TableInput — the JWT must be manually entered as a key/value row
- No automatic JWT injection from Langflow global variables (e.g.
X-Langflow-Global-Var-JWT)
src/api/docling.py health proxy
- JWT forwarding to Docling health endpoint is also gated on
IBM_AUTH_ENABLED
Goals
Proposed Changes
1. src/services/docling_service.py — _get_auth_headers()
Remove the IBM_AUTH_ENABLED guard so that the Authorization header is always forwarded when an auth_header is provided:
def _get_auth_headers(self, user_id=None, auth_header=None):
headers = {}
if auth_header:
headers["Authorization"] = auth_header
if IBM_AUTH_ENABLED and user_id:
headers["X-Tenant-Id"] = user_id
return headers
2. src/services/docling_service.py — check_task_status() and fetch_task_result()
Add optional auth_header / user_id parameters and call _get_auth_headers() before each request.
3. flows/components/docling_remote.py — Langflow component
Add a hidden StrInput for jwt that reads from the Langflow global variable JWT, and merge it into the headers passed to every httpx.Client call as Authorization: Bearer <jwt> (or raw value if it already contains the scheme).
4. src/api/docling.py — health proxy
Forward Authorization header whenever user.jwt_token is available, not just when IBM_AUTH_ENABLED:
if user and user.jwt_token:
headers["Authorization"] = user.jwt_token
if IBM_AUTH_ENABLED and user and user.user_id:
headers["X-Tenant-Id"] = user.user_id
References
Overview
OpenRAG already passes a universal JWT as the
Authorizationheader when making OpenSearch calls on SaaS. However, Docling Serve authentication is currently gated exclusively behind theIBM_AUTH_ENABLEDflag (introduced in #1551 and #1545 by @lucaseduoli). Non-IBM SaaS deployments therefore send Docling requests with noAuthorizationheader.This issue tracks bringing Docling Serve auth to parity with OpenSearch by propagating the universal JWT on every Docling call, regardless of deployment type.
Related to #1717.
Problem
DoclingService._get_auth_headers()(src/services/docling_service.py)Methods that bypass
_get_auth_headers()entirelycheck_task_status()— status polling uses the default client with no auth headersfetch_task_result()— result retrieval uses the default client with no auth headersflows/components/docling_remote.py(Langflow component)api_headersTableInput — the JWT must be manually entered as a key/value rowX-Langflow-Global-Var-JWT)src/api/docling.pyhealth proxyIBM_AUTH_ENABLEDGoals
IBM_AUTH_ENABLEDgate from_get_auth_headers()so that the JWTAuthorizationheader is forwarded universally (when a JWT/auth header is present), regardless of deployment type.check_task_status()— the status polling loop needs the caller to supply (and the service to forward) the JWT.fetch_task_result()— same as above.DoclingRemoteComponent(flows/components/docling_remote.py) to automatically inject the JWT from a Langflow global variable (e.g.JWT) into the outboundAuthorizationheader rather than relying on manualapi_headerstable input.src/api/docling.py) to forward JWT unconditionally when present.Proposed Changes
1.
src/services/docling_service.py—_get_auth_headers()Remove the
IBM_AUTH_ENABLEDguard so that theAuthorizationheader is always forwarded when anauth_headeris provided:2.
src/services/docling_service.py—check_task_status()andfetch_task_result()Add optional
auth_header/user_idparameters and call_get_auth_headers()before each request.3.
flows/components/docling_remote.py— Langflow componentAdd a hidden
StrInputforjwtthat reads from the Langflow global variableJWT, and merge it into the headers passed to everyhttpx.Clientcall asAuthorization: Bearer <jwt>(or raw value if it already contains the scheme).4.
src/api/docling.py— health proxyForward
Authorizationheader wheneveruser.jwt_tokenis available, not just whenIBM_AUTH_ENABLED:References
src/services/docling_service.py—DoclingServiceflows/components/docling_remote.py— Langflow Docling Serve componentsrc/api/docling.py— Docling health proxy endpoint