Skip to content

feat: Use universal JWT for Docling Serve authentication (parity with OpenSearch SaaS) #1722

@coderabbitai

Description

@coderabbitai

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

  • Remove the IBM_AUTH_ENABLED gate from _get_auth_headers() so that the JWT Authorization header is forwarded universally (when a JWT/auth header is present), regardless of deployment type.
  • Propagate auth headers to check_task_status() — the status polling loop needs the caller to supply (and the service to forward) the JWT.
  • Propagate auth headers to fetch_task_result() — same as above.
  • Update DoclingRemoteComponent (flows/components/docling_remote.py) to automatically inject the JWT from a Langflow global variable (e.g. JWT) into the outbound Authorization header rather than relying on manual api_headers table input.
  • Update the health proxy (src/api/docling.py) to forward JWT unconditionally when present.
  • Add/update tests to cover the non-IBM JWT-auth path for both the backend service and the Langflow component.

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.pycheck_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

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions