Skip to content

Kerberos authentication does not work due to client.execute calling client.request multiple times with the same Authorization header #93

@brouberol

Description

@brouberol

When a Client is instanciated with a custom_auth header that contains a Kerberos Authorization: Negotiate <base64 authenticator> header, that header is used in every client.request method call. However, by definition, these authenticator tokens can be used only once.

This is problematic, because client.execute calls client.request multiple times, to first submit the query and then poll for the result, with the same headers.

Here's an example to demonstrate the issue, with curl, and with the nodejs kerberos library to get the kerberos header.

> const kerberos =  await import("kerberos");
undefined
> krb = await  kerberos.Kerberos.initializeClient("presto@presto.example.com", {mechOID: kerberos.GSS_MECH_OID_KRB5, principal: "HTTP/app.example.com@REALM"});
KerberosClient {}
> await krb.step("")
'YIIDT...wmRFK9Q=='
$ curl -s \
  --service presto \
  -H "Authorization: Negotiate YIIDT...wmRFK9Q==" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'X-Presto-User: brouberol' \
  -d '{"query": "SELECT 1"}'  \
   https://presto.example.org:8281/v1/statement
...
{
  "id": "20251121_091834_00661_5x3zf",
  "infoUri": "https://presto.example.org:8281/ui/query.html?20251121_091834_00661_5x3zf",
  "nextUri": "https://presto.example.org:8281/v1/statement/queued/20251121_091834_00661_5x3zf/1?slug=xe92f5bb4728e4654a3d3577411f1aab3",
  "stats": {
    "state": "WAITING_FOR_PREREQUISITES",
    "waitingForPrerequisites": true,
    "queued": false,
    "scheduled": false,
    "nodes": 0,
    "totalSplits": 0,
    "queuedSplits": 0,
    "runningSplits": 0,
    "completedSplits": 0,
    "cpuTimeMillis": 0,
    "wallTimeMillis": 0,
    "waitingForPrerequisitesTimeMillis": 0,
    "queuedTimeMillis": 0,
    "elapsedTimeMillis": 0,
    "processedRows": 0,
    "processedBytes": 0,
    "peakMemoryBytes": 0,
    "peakTotalMemoryBytes": 0,
    "peakTaskTotalMemoryBytes": 0,
    "spilledBytes": 0
  },
  "warnings": []
}

However, any subsequent call using the same token returns an HTTP 401:

$ curl -s \
  --service presto \
  -H "Authorization: Negotiate YIIDT...wmRFK9Q=="  \
  -H 'Content-Type: application/json'  \
  -H 'Accept: application/json' \
  -H 'X-Presto-User: brouberol' 
  -X GET  \
  https://presto.example.org:8281/v1/statement/queued/20251121_091834_00661_5x3zf/1?slug=xe92f5bb4728e4654a3d3577411f1aab3 -v
...
* upload completely sent off: 21 out of 21 bytes
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Authentication failed for token: TOK==
< Date: Fri, 21 Nov 2025 09:20:29 GMT
< WWW-Authenticate: Negotiate
< Cache-Control: must-revalidate,no-cache,no-store
< Content-Length: 0
<
* Connection #0 to host an-coord1003.eqiad.wmnet left intact

This is why curl has a --negotiate flag, that uses libgssapi to negotiate a new authenticator for each request. The issue here is that presto-client-node assumes that the user must provide the client with a kerberos header, and does not provide them with a pre-request hook that could be used to renew the authorization header.

Another strategy could be to rely on a kerberos-aware http library that would perform the negotiation for the user.

In the current state though, Kerberos support does not work AFAICT.

Thanks and happy to hear your thoughts!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions