{"record":{"id":"fa8e115598ed8256","repo":"spring-projects/spring-security","slug":"digestauthenticationfilter-missingauth","errorCode":"DigestAuthenticationFilter.missingAuth","errorMessage":"Missing mandatory digest value; received header {0}","messagePattern":"Missing mandatory digest value; received header (.+?)","errorType":"exception","errorClass":"BadCredentialsException","httpStatus":401,"severity":"error","filePath":"web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java","lineNumber":368,"sourceCode":"\t\t\tlogger.debug(\n\t\t\t\t\tLogMessage.format(\"Extracted username: '%s'; realm: '%s'; nonce: '%s'; uri: '%s'; response: '%s'\",\n\t\t\t\t\t\t\tthis.username, this.realm, this.nonce, this.uri, this.response));\n\t\t}\n\n\t\tvoid validateAndDecode(@Nullable String entryPointKey, @Nullable String expectedRealm)\n\t\t\t\tthrows BadCredentialsException {\n\t\t\t// Check all required parameters were supplied (ie RFC 2069)\n\t\t\tif ((this.username == null) || (this.realm == null) || (this.nonce == null) || (this.uri == null)\n\t\t\t\t\t|| (this.response == null)) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.missingMandatory\", new Object[] { this.section212response },\n\t\t\t\t\t\t\"Missing mandatory digest value; received header {0}\"));\n\t\t\t}\n\t\t\t// Check all required parameters for an \"auth\" qop were supplied (ie RFC 2617)\n\t\t\tif (\"auth\".equals(this.qop)) {\n\t\t\t\tif ((this.nc == null) || (this.cnonce == null)) {\n\t\t\t\t\tlogger.debug(LogMessage.format(\"extracted nc: '%s'; cnonce: '%s'\", this.nc, this.cnonce));\n\t\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\t\"DigestAuthenticationFilter.missingAuth\", new Object[] { this.section212response },\n\t\t\t\t\t\t\t\"Missing mandatory digest value; received header {0}\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Check realm name equals what we expected\n\t\t\tif (!this.realm.equals(expectedRealm)) {\n\t\t\t\tthrow new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage(\n\t\t\t\t\t\t\"DigestAuthenticationFilter.incorrectRealm\", new Object[] { this.realm, expectedRealm },\n\t\t\t\t\t\t\"Response realm name '{0}' does not match system realm name of '{1}'\"));\n\t\t\t}\n\t\t\t// Check nonce was Base64 encoded (as sent by DigestAuthenticationEntryPoint)\n\t\t\tfinal byte[] nonceBytes;\n\t\t\ttry {\n\t\t\t\tnonceBytes = Base64.getDecoder().decode(this.nonce.getBytes());\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException ex) {\n\t\t\t\tthrow new BadCredentialsException(\n\t\t\t\t\t\tDigestAuthenticationFilter.this.messages.getMessage(\"DigestAuthenticationFilter.nonceEncoding\",","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java#L350-L386","documentation":"Same 'missing mandatory digest value' message but for the RFC 2617 'auth' qop path: when qop=auth is declared, the header must also contain nc (nonce count) and cnonce (client nonce). validateAndDecode throws this BadCredentialsException if either is null.","triggerScenarios":"A Digest Authorization header sets qop=\"auth\" but omits nc and/or cnonce. This happens when a client advertises a qop it doesn't fully implement, or builds the header from an incomplete parameter set.","commonSituations":"Clients that parse the server's qop options but never generate the client nonce/counter; header built by string concatenation missing nc/cnonce; upgraded servers now requiring qop=auth while clients were written for legacy RFC 2069 (no qop).","solutions":["Have the client include nc (e.g. 00000001) and a random cnonce whenever qop is present in the challenge response.","If the client only supports RFC 2069, configure/choose a server qop that matches, or fall back to no-qop digest, ensuring both sides agree.","Regenerate the digest response including cnonce and nc in the MD5 input (A2 includes qop, nc, cnonce for RFC 2617) so the header is self-consistent.","Log the full header from the exception message and diff each parameter against the RFC 2617 grammar."],"exampleFix":"// before\nAuthorization: Digest username=\"u\", realm=\"r\", nonce=\"n\", uri=\"/\", qop=\"auth\", response=\"d\"\n// after\nAuthorization: Digest username=\"u\", realm=\"r\", nonce=\"n\", uri=\"/\", qop=\"auth\", nc=\"00000001\", cnonce=\"0a4f113b\", response=\"d\"","handlingStrategy":"validation","validationCode":"Map<String,String> p = parseDigestParams(header);\nif (\"auth\".equals(p.get(\"qop\")) && (p.get(\"nc\") == null || p.get(\"cnonce\") == null)) {\n    throw new IllegalStateException(\"qop=auth requires nc and cnonce in the Digest header\");\n}\n","typeGuard":null,"tryCatchPattern":"try {\n    chain.doFilter(request, response);\n} catch (BadCredentialsException e) {\n    if (e.getMessage().contains(\"Missing mandatory digest value\")) {\n        response.sendError(401, \"qop=auth requires nc and cnonce\");\n    }\n}","preventionTips":["Only advertise qop support your client fully implements","Generate a random cnonce and increment nc per request whenever qop is present","Keep client and server in the same RFC level (2617 qop vs 2069 no-qop)","Diff your header against a working digest client's output"],"tags":["spring-security","digest-auth","qop","bad-credentials"],"backgroundTag":"missing-required-argument","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}