@@ -150,17 +150,21 @@ public void invalidRspAuthIsRejected() throws Exception {
150150 }
151151
152152 /**
153- * RFC 7616 §3.5 defines {@code A2} for {@code qop=auth-int} as {@code ":" request-uri ":" H(entity-body)}
154- * over the <em>response</em> entity-body. That body has not been received when the {@code
155- * Authentication-Info} header is processed, so the expected value simply cannot be derived there. The
156- * client must therefore fall back to warn-and-deliver for auth-int instead of enforcing the auth-only
157- * formula, which would abort against a perfectly conformant server.
153+ * RFC 7616 Section 3.5 defines {@code A2} for {@code qop=auth-int} as {@code ":" request-uri ":"
154+ * H(entity-body)} over the <em>response</em> entity-body. That body has not arrived when the {@code
155+ * Authentication-Info} header is processed, so the expected rspauth cannot be derived there at all.
158156 * <p>
159- * The server arm here computes rspauth <em>from the RFC</em> rather than by mirroring the client, which is
160- * what makes this test able to catch the mismatch at all.
157+ * The client used to negotiate auth-int anyway and then report "cannot verify", which delivered the
158+ * response with mutual authentication silently skipped for the whole exchange. Since the peer chooses
159+ * the challenge, offering {@code qop="auth-int"} on its own was a one-word way to switch mutual
160+ * authentication off. The client now declines to negotiate a mode whose rspauth it cannot check.
161+ * <p>
162+ * Interoperability cost, deliberately accepted: a server offering auth-int and nothing else can no
163+ * longer be authenticated against. {@code auth,auth-int} is unaffected, because auth is preferred and
164+ * its rspauth is verified.
161165 */
162166 @ RepeatedIfExceptionsTest (repeats = 5 )
163- public void authIntRspAuthIsNotEnforcedAgainstAConformantServer () throws Exception {
167+ public void anAuthIntOnlyChallengeIsNotAnsweredWithAuthInt () throws Exception {
164168 restartServer (new AuthIntRspAuthHandler ());
165169
166170 try (AsyncHttpClient client = asyncHttpClient ()) {
@@ -169,12 +173,12 @@ public void authIntRspAuthIsNotEnforcedAgainstAConformantServer() throws Excepti
169173 .execute ();
170174 Response resp = f .get (60 , TimeUnit .SECONDS );
171175 assertNotNull (resp );
172- assertEquals ( HttpServletResponse . SC_OK , resp . getStatusCode ());
173- assertEquals (AuthIntRspAuthHandler . BODY , resp .getResponseBody ());
174- // The exchange really did negotiate auth-int; the client did not quietly fall back to auth.
175- assertNotNull ( resp .getHeader ("X-Auth" ) );
176- assertTrue (resp . getHeader ( "X-Auth" ) .contains ("qop=auth-int" ),
177- "expected the request to have been sent with qop= auth-int but got : " + resp . getHeader ( "X-Auth" ) );
176+ // The server only accepts qop=auth-int, so declining it means the exchange is not authenticated.
177+ assertEquals (HttpServletResponse . SC_UNAUTHORIZED , resp .getStatusCode (),
178+ "an auth-int-only challenge must not be answered, since its rspauth cannot be verified" );
179+ String sent = resp .getHeader ("X-Auth" );
180+ assertTrue (sent == null || ! sent .contains ("qop=auth-int" ),
181+ "the client must not negotiate auth-int: " + sent );
178182 }
179183 }
180184
0 commit comments