Credentials are never sent to servers that omit WWW-Authenticate on 401 (e.g. Yahoo Calendar)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 62/100
Línea de trabajo
Start in davclient.py, where the issue says the request handling gates the 401 retry on WWW-Authenticate; read that branch and the surrounding auth setup first. Check how retries are limited so a repeated 401 cannot loop. Done when a 401 without the header can trigger one Basic-auth retry when credentials are present and no auth object exists, while preserving the existing failure behavior otherwise.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
When a server responds 401 without a WWW-Authenticate header, DAVClient never
builds an auth object and never transmits the supplied credentials. The bare 401
propagates to the caller as AuthorizationError, which is indistinguishable from a
genuinely rejected password — but the password was never sent.
Yahoo Calendar's CalDAV server (caldav.calendar.yahoo.com) behaves this way. RFC 7235
§3.1 requires WWW-Authenticate on a 401, so this is a server bug, but it is a server
bug that currently makes Yahoo unreachable through this library even with valid
credentials.
Affected versions
Reproduced on 2.1.0 and 2.2.6.
Reproduction — no account or credentials required
The deliberately wrong password is the point: it demonstrates that self.auth is still
None after the request, meaning nothing was ever transmitted.
import caldav
client = caldav.DAVClient(
url="https://caldav.calendar.yahoo.com/",
username="[email protected]",
password="does-not-matter",
)
try:
client.principal()
except Exception as exc:
print(type(exc).__name__) # AuthorizationError
print(client.auth) # None <- credential never sent
On 2.1.0 this additionally logs a CRITICAL with an lxml.etree.XMLSyntaxError
traceback, because Yahoo's error body is JSON where XML is expected:
CRITICAL:root:Expected some valid XML from the server, but got this:
b'{"error":{"code": 4003, "message":"Invalid User credential"}}'
The exception is caught and AuthorizationError is raised as above, so this is noise
rather than a second failure — but it is alarming in downstream logs and would disappear
along with the root cause. 2.2.6 does not log it.
With auth_type="basic" the auth object is built up front and the request succeeds
(verified against a real Yahoo account: 207, principal /principals/users/<user>/,
and all calendars enumerate normally).
Server behaviour
$ curl -s -D - -o /dev/null -X PROPFIND https://caldav.calendar.yahoo.com/ -H 'Depth: 0'
HTTP/2 401
content-type: application/json
server: ATS
No WWW-Authenticate header in the response. The server is otherwise a functioning
CalDAV endpoint:
$ curl -s -D - -o /dev/null -X OPTIONS https://caldav.calendar.yahoo.com/
HTTP/2 200
dav: 1, 3, calendar-access
allow: HEAD, MKCOL, POST, PROPFIND, ACL, COPY, REPORT, OPTIONS, PUT, DELETE, MKCALENDAR, MOVE, GET, PROPPATCH
Cause
davclient.py (2.1.0, ~line 950) gates the retry-with-credentials path on the header
being present:
if (
r.status_code == 401
and "WWW-Authenticate" in r_headers
and not self.auth
and (self.username or self.password)
):
auth_types = self.extract_auth_types(r_headers["WWW-Authenticate"])
self.build_auth_object(auth_types)
...
return self.request(url, method, body, headers)
When the header is absent the branch is skipped entirely, so build_auth_object() is
never reached and the 401 is returned as-is.
Proposed fix
When a 401 carries no WWW-Authenticate, and credentials were supplied, and no auth
object exists yet, attempt basic once rather than giving up:
elif (
r.status_code == 401
and "WWW-Authenticate" not in r_headers
and not self.auth
and self.username
and self.password
):
# RFC 7235 requires WWW-Authenticate on a 401, but some servers (Yahoo
# Calendar) omit it. Without a declared scheme we cannot negotiate, so
# try basic once before surfacing the 401.
self.auth_type = "basic"
self.build_auth_object()
return self.request(url, method, body, headers)
Guarding on a retry flag would prevent a loop against a server that returns 401 to the
authenticated request as well.
Note on preemptive credentials
This does send Basic credentials to a server that never declared support for them. That
is already what auth_type="basic" does today, it only happens after an explicit 401,
and the connection is TLS. If sending unprompted credentials is considered too
aggressive as a default, an opt-in flag (allow_unprompted_basic=True) would still be a
large improvement over the current behaviour, because the failure today is silent and
misattributed.
Why this is worth fixing rather than documenting
Downstream consumers surface the resulting exception as a rejected credential. Home
Assistant's CalDAV integration reports "invalid authentication", which sends users to
regenerate app passwords, retype them, and suspect their clipboard — none of which can
work, because the credential is never transmitted. The library holds the only evidence
that distinguishes the two cases.
Workaround for anyone who lands here
caldav.DAVClient(url=..., username=..., password=..., auth_type="basic")
Home Assistant's config flow does not expose auth_type, so HA users additionally need a
small custom component wrapping DAVClient.__init__ to inject it.
- Lenguaje dominante
- Python
- Estrellas
- 412
- Forks
- 113
- Merge medio
- 9 h 52 min
- PR fusionados (30 d)
- 64
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de python-caldav/caldav
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
python-caldav/caldav#792 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
python-caldav/caldav#788 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
python-caldav/caldav#687 ·
Los mantenedores suelen responder en 1 día
-
ical + caldavAbierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
python-caldav/caldav#825 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
python-caldav/caldav#823 ·
Los mantenedores suelen responder en 1 día
Todos los issues de python-caldav/caldav
Issues similares
-
request-theme
Dificultad 1/5 Menos de una hora Aptitud para principiantes 66/100
LizardByte/ThemerrDB#9575 ·
Los mantenedores suelen responder en 1 día
-
Author Page: noshin-nawalAbiertoauthor-page correction
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
acl-org/acl-anthology#10599 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
zilliztech/memsearch#818 ·
Los mantenedores suelen responder en 1 día
-
[FAQ] Commit ID for ProjectsPosiblemente ocupada @github-actions la tomó hoy. Abiertofaq-proposal
Dificultad 1/5 Menos de una hora Aptitud para principiantes 84/100
DataTalksClub/faq#503 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
README.zh-CN/ja/es still hard-code 501 test counts after README.md went count-freePosiblemente ocupada @Aaqibhafeezkhan la tomó hoy. Abiertobeginner documentation good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
BistaDinesh03/critique#164 · 1 comentario ·
Los mantenedores suelen responder en 1 día