{"id":"511f23d191ead1fd","repo":"aio-libs/aiohttp","slug":"malformed-digest-auth-challenge-missing-nonce-p","errorCode":null,"errorMessage":"Malformed Digest auth challenge: Missing 'nonce' parameter","messagePattern":"Malformed Digest auth challenge: Missing 'nonce' parameter","errorType":"exception","errorClass":"ClientError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_middleware_digest_auth.py","lineNumber":248,"sourceCode":"            url: The request URL\n            body: The request body (used for qop=auth-int)\n\n        Returns:\n            A fully formatted Digest authorization header string\n\n        Raises:\n            ClientError: If the challenge is missing required parameters or\n                         contains unsupported values\n\n        \"\"\"\n        challenge = self._challenge\n        if \"realm\" not in challenge:\n            raise ClientError(\n                \"Malformed Digest auth challenge: Missing 'realm' parameter\"\n            )\n\n        if \"nonce\" not in challenge:\n            raise ClientError(\n                \"Malformed Digest auth challenge: Missing 'nonce' parameter\"\n            )\n\n        # Empty realm values are allowed per RFC 7616 (SHOULD, not MUST, contain host name)\n        realm = challenge[\"realm\"]\n        nonce = challenge[\"nonce\"]\n\n        # Empty nonce values are not allowed as they are security-critical for replay protection\n        if not nonce:\n            raise ClientError(\n                \"Security issue: Digest auth challenge contains empty 'nonce' value\"\n            )\n\n        qop_raw = challenge.get(\"qop\", \"\")\n        # Preserve original algorithm case for response while using uppercase for processing\n        algorithm_original = challenge.get(\"algorithm\", \"MD5\")\n        algorithm = algorithm_original.upper()\n        opaque = challenge.get(\"opaque\", \"\")","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_middleware_digest_auth.py#L230-L266","documentation":"Raised as `ClientError` in `DigestAuthMiddleware._encode` (client_middleware_digest_auth.py:247-250) when the parsed challenge lacks a `nonce` key. The server's nonce is essential to Digest auth: it's part of H(A1)/H(A2), the client counter (`nc`) is tracked against it, and it's replay-protection material (client_middleware_digest_auth.py:215-216). A separate guard at line 257-260 also rejects an *empty* nonce string for the same security reason.","triggerScenarios":"Server returns `WWW-Authenticate: Digest realm=\"x\"` with no `nonce=`; the parser failed to extract nonce due to quoting/format issues; nonce field is present but empty.","commonSituations":"Server misconfiguration; buggy auth server omitting nonce; intermediary stripping challenge parameters; a Digest challenge from a server that doesn't fully implement RFC 7616.","solutions":["Capture and inspect the raw `WWW-Authenticate` header.","Fix the server to include a non-empty `nonce=` in the Digest challenge.","Ensure the challenge is well-formed (proper quoting: `nonce=\"abc123\"`).","Catch `ClientError` and surface a clear auth-failure rather than retrying."],"exampleFix":"// before\n# server sends: WWW-Authenticate: Digest realm=\"r\"   (no nonce)\n# -> ClientError: Missing 'nonce'\n// after (server-side)\n# WWW-Authenticate: Digest realm=\"r\", nonce=\"opaque-random-value\", qop=\"auth\"","handlingStrategy":"try-catch","validationCode":"from aiohttp import hdrs\n\ndef challenge_has_nonce(resp_headers) -> bool:\n    auth = resp_headers.get(hdrs.WWW_AUTHENTICATE, '')\n    return 'nonce=' in auth.lower()","typeGuard":null,"tryCatchPattern":"from aiohttp import ClientError\n\ntry:\n    resp = await session.get(url, middlewares=[digest_mw])\nexcept ClientError as e:\n    if \"Missing 'nonce'\" in str(e):\n        log.error('server Digest challenge lacks nonce; check WWW-Authenticate')\n    raise","preventionTips":["Ensure the server's Digest challenge includes a non-empty `nonce`.","Treat a missing/empty nonce as a server-side defect, not a client bug.","Log 401 response headers to audit challenge completeness."],"tags":["client","digest-auth","authentication","server-misbehavior","rfc","security"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}