mirror of
https://github.com/Such-Software/adaptive-multi-protocol-gateway.git
synced 2026-10-17 07:31:32 +02:00
Send X-Request-ID with the case Dynadot requires (#3)
Every Dynadot request this provider has ever made was rejected with "the X-Signature provided is not valid". The signature was correct. The header name was not. urllib capitalises header names when they go through add_header, which the Request constructor does for everything passed to it, so X-Request-ID left as X-request-id. Header names are case-insensitive under RFC 9110 and Dynadot's gateway is not: it matches X-Request-ID exactly, did not find the rewritten form, and computed the signature with an empty request id while we computed ours with the UUID. A correct signature over a different string, reported as a bad signature. That message sends you to the algorithm and the credentials, which is where I spent the evening. The header we were plainly sending is the last thing anyone would suspect. Assigning the mapping after construction bypasses add_header and preserves the name. Confirmed against the live API: exact X-Request-ID is accepted, and X-Request-Id one letter different is rejected. Two tests, both of which fail against the previous code: the wire name keeps its case, and the id in the signature is the id in the header. The second is the invariant that actually broke, and it can break again without the first. Co-authored-by: jw <jw@jwmacbookm5.local>
This commit is contained in:
+12
-1
@@ -288,9 +288,20 @@ class DynadotDNSProvider:
|
||||
request = urllib.request.Request(
|
||||
self.api_base_url + path,
|
||||
data=request_body.encode("utf-8") if body is not None else None,
|
||||
headers=headers,
|
||||
method=method,
|
||||
)
|
||||
# Assigned rather than passed to the constructor, which routes every
|
||||
# header through add_header() and capitalises the name: X-Request-ID
|
||||
# becomes X-request-id on the wire.
|
||||
#
|
||||
# Header names are case-insensitive per RFC 9110, and Dynadot's gateway
|
||||
# is not. It matches X-Request-ID exactly, does not find the rewritten
|
||||
# form, and therefore computes the signature with an empty request id
|
||||
# while we computed ours with the UUID. The result is a valid signature
|
||||
# over a different string, reported as "the X-Signature provided is not
|
||||
# valid", which points at the algorithm and the credentials rather than
|
||||
# at a header we did send.
|
||||
request.headers = headers
|
||||
try:
|
||||
with self._opener(request, timeout=30) as response:
|
||||
raw = response.read().decode("utf-8")
|
||||
|
||||
Reference in New Issue
Block a user