Skip to content

gh-59515: Support multiple challenges in urllib Digest Auth - #160828

Merged
orsenthil merged 2 commits into
python:mainfrom
orsenthil:issues/59515
Nov 1, 2026
Merged

orsenthil merged 2 commits into
python:mainfrom
orsenthil:issues/59515

Conversation

@orsenthil

@orsenthil orsenthil commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

HTTPDigestAuthHandler and ProxyDigestAuthHandler only looked at the first WWW-Authenticate / Proxy-Authenticate header and took its first word as the scheme. As a result:

  • A Digest challenge was ignored if a Basic challenge came first, in the same header or in an earlier one.

  • An unsupported scheme listed first (for example Kerberos, Basic realm="x") raised ValueError. Because the Digest handler runs before the Basic handler, that also blocked HTTPBasicAuthHandler.

This PR changes only AbstractDigestAuthHandler.
AbstractBasicAuthHandler already handles multiple challenges since GH-18284 and is not modified.

  • http_error_auth_reqed() now reads all headers (get_all()) and uses the first Digest challenge found. It raises ValueError only when neither Digest nor Basic is offered; otherwise it returns None so the next handler can try.

  • A new private _parse_challenges() splits a header into challenges per RFC 9110 §11.6.1. A comma-separated element that starts with a word not followed by = begins a new challenge. Its quote/escape handling matches parse_http_list(). It makes a single linear pass with no regex.

  • retry_http_digest_auth() is unchanged. It receives the original text of the Digest challenge, so escapes are not processed twice. For a single-challenge header the string is the same as before.

🤖 Generated with Claude Code


I have reviewed the logic and changes done by claude. This is the most compact way to address requirement in the long pending issue gh-59515

`HTTPDigestAuthHandler` and `ProxyDigestAuthHandler` only looked at the
first `WWW-Authenticate` / `Proxy-Authenticate` header and took its
first word as the scheme. As a result:

* A Digest challenge was ignored if a Basic challenge came first, in the
  same header or in an earlier one.

* An unsupported scheme listed first (for example `Kerberos, Basic
  realm="x"`) raised `ValueError`. Because the Digest handler runs
  before the Basic handler, that also blocked `HTTPBasicAuthHandler`.

This PR changes only `AbstractDigestAuthHandler`.
`AbstractBasicAuthHandler` already handles multiple challenges since
pythonGH-18284 and is not modified.

* `http_error_auth_reqed()` now reads all headers (`get_all()`) and uses
  the first Digest challenge found. It raises `ValueError` only when
  neither Digest nor Basic is offered; otherwise it returns `None` so
  the next handler can try.

* A new private `_parse_challenges()` splits a header into challenges
  per RFC 9110 §11.6.1. A comma-separated element that starts with a
  word not followed by `=` begins a new challenge. Its quote/escape
  handling matches `parse_http_list()`. It makes a single linear pass
  with no regex.

* `retry_http_digest_auth()` is unchanged. It receives the original text
  of the Digest challenge, so escapes are not processed twice. For a
  single-challenge header the string is the same as before.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@orsenthil
orsenthil merged commit 529d66c into python:main Nov 1, 2026
53 checks passed
@orsenthil orsenthil added needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Nov 1, 2026
@miss-islington-app

Copy link
Copy Markdown

Thanks @orsenthil for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Thanks @orsenthil for the PR 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@orsenthil orsenthil added needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes and removed needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Nov 1, 2026
@miss-islington-app

Copy link
Copy Markdown

Thanks @orsenthil for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖 I'm not a witch! I'm not a witch!

@miss-islington-app

Copy link
Copy Markdown

Thanks @orsenthil for the PR 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@orsenthil orsenthil added needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes and removed needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Nov 2, 2026
@miss-islington-app

Copy link
Copy Markdown

Thanks @orsenthil for the PR 🌮🎉.. I'm working now to backport this PR to: 3.15.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Thanks @orsenthil for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14.
🐍🍒⛏🤖

@bedevere-app

bedevere-app Bot commented Nov 2, 2026

Copy link
Copy Markdown

GH-161160 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 bugs and security fixes label Nov 2, 2026
@bedevere-app

bedevere-app Bot commented Nov 2, 2026

Copy link
Copy Markdown

GH-161161 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Nov 2, 2026
orsenthil added a commit that referenced this pull request Nov 2, 2026
…H-160828) (#161160)

gh-59515: Support multiple challenges in urllib Digest Auth (GH-160828)

`HTTPDigestAuthHandler` and `ProxyDigestAuthHandler` only looked at the
first `WWW-Authenticate` / `Proxy-Authenticate` header and took its
first word as the scheme. As a result:

* A Digest challenge was ignored if a Basic challenge came first, in the
  same header or in an earlier one.

* An unsupported scheme listed first (for example `Kerberos, Basic
  realm="x"`) raised `ValueError`. Because the Digest handler runs
  before the Basic handler, that also blocked `HTTPBasicAuthHandler`.

This PR changes only `AbstractDigestAuthHandler`.
`AbstractBasicAuthHandler` already handles multiple challenges since
GH-18284 and is not modified.

* `http_error_auth_reqed()` now reads all headers (`get_all()`) and uses
  the first Digest challenge found. It raises `ValueError` only when
  neither Digest nor Basic is offered; otherwise it returns `None` so
  the next handler can try.

* A new private `_parse_challenges()` splits a header into challenges
  per RFC 9110 §11.6.1. A comma-separated element that starts with a
  word not followed by `=` begins a new challenge. Its quote/escape
  handling matches `parse_http_list()`. It makes a single linear pass
  with no regex.

* `retry_http_digest_auth()` is unchanged. It receives the original text
  of the Digest challenge, so escapes are not processed twice. For a
  single-challenge header the string is the same as before.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
(cherry picked from commit 529d66c)

Co-authored-by: Senthil Kumaran <senthil@python.org>
orsenthil added a commit that referenced this pull request Nov 2, 2026
…H-160828) (#161161)

gh-59515: Support multiple challenges in urllib Digest Auth (GH-160828)

`HTTPDigestAuthHandler` and `ProxyDigestAuthHandler` only looked at the
first `WWW-Authenticate` / `Proxy-Authenticate` header and took its
first word as the scheme. As a result:

* A Digest challenge was ignored if a Basic challenge came first, in the
  same header or in an earlier one.

* An unsupported scheme listed first (for example `Kerberos, Basic
  realm="x"`) raised `ValueError`. Because the Digest handler runs
  before the Basic handler, that also blocked `HTTPBasicAuthHandler`.

This PR changes only `AbstractDigestAuthHandler`.
`AbstractBasicAuthHandler` already handles multiple challenges since
GH-18284 and is not modified.

* `http_error_auth_reqed()` now reads all headers (`get_all()`) and uses
  the first Digest challenge found. It raises `ValueError` only when
  neither Digest nor Basic is offered; otherwise it returns `None` so
  the next handler can try.

* A new private `_parse_challenges()` splits a header into challenges
  per RFC 9110 §11.6.1. A comma-separated element that starts with a
  word not followed by `=` begins a new challenge. Its quote/escape
  handling matches `parse_http_list()`. It makes a single linear pass
  with no regex.

* `retry_http_digest_auth()` is unchanged. It receives the original text
  of the Digest challenge, so escapes are not processed twice. For a
  single-challenge header the string is the same as before.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
(cherry picked from commit 529d66c)

Co-authored-by: Senthil Kumaran <senthil@python.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant