Skip to content

Commit 2a9e489

Browse files
committed
Fix intermittent hang in WebAuthn4JHandlerTest (#2923)
testRequestBuffer called requestAction.accept(req) which invoked req.send(body), ending the HTTP request, then immediately called req.send() again on the same already-ended request. The second call failed, causing TestUtils.onSuccess to throw AssertionError inside the Vert.x event loop handler. Because that exception escaped without completing the Promise, promise.future().await() blocked indefinitely. Replace testRequestBuffer with direct webClient calls (consistent with WebTestBase.testRequest), and make the testStorage.find() assertions synchronous so they are actually enforced. Signed-off-by: Thomas Segismont <[email protected]>
1 parent 8b7793a commit 2a9e489

1 file changed

Lines changed: 44 additions & 101 deletions

File tree

vertx-web/src/test/java/io/vertx/ext/web/tests/handler/WebAuthn4JHandlerTest.java

Lines changed: 44 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,8 @@
1919
import io.vertx.core.Context;
2020
import io.vertx.core.Future;
2121
import io.vertx.core.Handler;
22-
import io.vertx.core.Promise;
2322
import io.vertx.core.buffer.Buffer;
24-
import io.vertx.core.http.HttpClientRequest;
25-
import io.vertx.core.http.HttpClientResponse;
2623
import io.vertx.core.http.HttpHeaders;
27-
import io.vertx.core.http.HttpMethod;
28-
import io.vertx.ext.web.client.HttpResponse;
29-
import io.vertx.core.http.RequestOptions;
3024
import io.vertx.core.internal.ContextInternal;
3125
import io.vertx.core.json.JsonObject;
3226
import io.vertx.ext.auth.webauthn4j.RelyingParty;
@@ -35,12 +29,12 @@
3529
import io.vertx.ext.auth.webauthn4j.WebAuthn4J;
3630
import io.vertx.ext.auth.webauthn4j.WebAuthn4JOptions;
3731
import io.vertx.ext.web.RoutingContext;
32+
import io.vertx.ext.web.client.HttpResponse;
3833
import io.vertx.ext.web.handler.BodyHandler;
3934
import io.vertx.ext.web.handler.SessionHandler;
4035
import io.vertx.ext.web.handler.WebAuthn4JHandler;
4136
import io.vertx.ext.web.sstore.LocalSessionStore;
4237
import io.vertx.ext.web.tests.WebTestBase;
43-
import io.vertx.test.core.TestUtils;
4438

4539
import org.junit.jupiter.api.BeforeEach;
4640
import org.junit.jupiter.api.Test;
@@ -84,7 +78,6 @@
8478
import java.util.Collections;
8579
import java.util.List;
8680
import java.util.Set;
87-
import java.util.function.Consumer;
8881
import java.util.stream.Collectors;
8982

9083
public class WebAuthn4JHandlerTest extends WebTestBase {
@@ -227,22 +220,15 @@ public void testRegisterAndLogin() throws Exception {
227220
}
228221

229222
private String testRegistration(ClientPlatform clientPlatform) throws Exception {
230-
231-
String[] obtainedChallenge = new String[1];
232-
String[] obtainedCookie = new String[1];
233223
JsonObject registerRequest = new JsonObject()
234224
.put("name", username);
235-
testRequestBuffer(HttpMethod.POST, "/webauthn/register", req -> {
236-
req.send(registerRequest.encode());
237-
}, resp -> {
238-
String cookie = resp.getHeader(HttpHeaders.SET_COOKIE);
239-
obtainedCookie[0] = extractVertxSessionCookie(cookie);
240-
}, 200, "OK", buffer -> {
241-
JsonObject jsonObject = buffer.toJsonObject();
242-
obtainedChallenge[0] = jsonObject.getString("challenge");
243-
});
244-
245-
DefaultChallenge challenge = new DefaultChallenge(obtainedChallenge[0]);
225+
HttpResponse<Buffer> regResp = testRequest(
226+
webClient.post("/webauthn/register").sendBuffer(Buffer.buffer(registerRequest.encode())),
227+
200, "OK");
228+
String obtainedCookie = extractVertxSessionCookie(regResp.getHeader(HttpHeaders.SET_COOKIE.toString()));
229+
String obtainedChallenge = regResp.body().toJsonObject().getString("challenge");
230+
231+
DefaultChallenge challenge = new DefaultChallenge(obtainedChallenge);
246232
RegistrationRequest registrationRequest = createRegistrationRequest(clientPlatform, origin.getHost(), challenge, username, displayName);
247233
// dummy request
248234
JsonObject request = new JsonObject()
@@ -253,26 +239,21 @@ private String testRegistration(ClientPlatform clientPlatform) throws Exception
253239
.put("attestationObject", Base64UrlUtil.encodeToString(registrationRequest.getAttestationObject()))
254240
.put("clientDataJSON", Base64UrlUtil.encodeToString(registrationRequest.getClientDataJSON())));
255241

256-
HttpResponse<Buffer> resp = testRequest(webClient.post("/webauthn/callback")
257-
.putHeader(HttpHeaders.COOKIE.toString(), obtainedCookie[0])
242+
HttpResponse<Buffer> callbackResp = testRequest(webClient.post("/webauthn/callback")
243+
.putHeader(HttpHeaders.COOKIE.toString(), obtainedCookie)
258244
.sendBuffer(Buffer.buffer(request.encode())), 204, "No Content");
259-
String cookie = resp.getHeader(HttpHeaders.SET_COOKIE.toString());
260-
obtainedCookie[0] = extractVertxSessionCookie(cookie);
261-
262-
testStorage.find(username, null)
263-
.onSuccess(authenticators -> {
264-
assertNotNull(authenticators);
265-
assertEquals(1, authenticators.size());
266-
Authenticator authenticator = authenticators.get(0);
267-
// Check username, credid, counter, publicKey
268-
assertEquals(username, authenticator.getUsername());
269-
assertEquals(credId, authenticator.getCredID());
270-
assertEquals(1, authenticator.getCounter());
271-
assertEquals(publicKey, authenticator.getPublicKey());
272-
})
273-
.onFailure(x -> fail("Well that did not work"));
274-
275-
return obtainedCookie[0];
245+
obtainedCookie = extractVertxSessionCookie(callbackResp.getHeader(HttpHeaders.SET_COOKIE.toString()));
246+
247+
List<Authenticator> authenticators = testStorage.find(username, null).await();
248+
assertNotNull(authenticators);
249+
assertEquals(1, authenticators.size());
250+
Authenticator authenticator = authenticators.get(0);
251+
assertEquals(username, authenticator.getUsername());
252+
assertEquals(credId, authenticator.getCredID());
253+
assertEquals(1, authenticator.getCounter());
254+
assertEquals(publicKey, authenticator.getPublicKey());
255+
256+
return obtainedCookie;
276257
}
277258

278259
private String extractVertxSessionCookie(String cookie) {
@@ -332,21 +313,15 @@ private RegistrationRequest createRegistrationRequest(ClientPlatform clientPlatf
332313
}
333314

334315
private String testAuthentication(ClientPlatform clientPlatform) throws Exception {
335-
String[] obtainedChallenge = new String[1];
336-
String[] obtainedCookie = new String[1];
337-
JsonObject registerRequest = new JsonObject()
316+
JsonObject loginRequest = new JsonObject()
338317
.put("name", username);
339-
testRequestBuffer(HttpMethod.POST, "/webauthn/login", req -> {
340-
req.send(registerRequest.encode());
341-
}, resp -> {
342-
String cookie = resp.getHeader(HttpHeaders.SET_COOKIE);
343-
obtainedCookie[0] = extractVertxSessionCookie(cookie);
344-
}, 200, "OK", buffer -> {
345-
JsonObject jsonObject = buffer.toJsonObject();
346-
obtainedChallenge[0] = jsonObject.getString("challenge");
347-
});
348-
349-
DefaultChallenge challenge = new DefaultChallenge(obtainedChallenge[0]);
318+
HttpResponse<Buffer> loginResp = testRequest(
319+
webClient.post("/webauthn/login").sendBuffer(Buffer.buffer(loginRequest.encode())),
320+
200, "OK");
321+
String obtainedCookie = extractVertxSessionCookie(loginResp.getHeader(HttpHeaders.SET_COOKIE.toString()));
322+
String obtainedChallenge = loginResp.body().toJsonObject().getString("challenge");
323+
324+
DefaultChallenge challenge = new DefaultChallenge(obtainedChallenge);
350325
AuthenticationRequest authenticationRequest = createAuthenticationRequest(clientPlatform, origin.getHost(), challenge, username, displayName);
351326
// dummy request
352327
JsonObject request = new JsonObject()
@@ -358,26 +333,21 @@ private String testAuthentication(ClientPlatform clientPlatform) throws Exceptio
358333
.put("authenticatorData", Base64UrlUtil.encodeToString(authenticationRequest.getAuthenticatorData()))
359334
.put("clientDataJSON", Base64UrlUtil.encodeToString(authenticationRequest.getClientDataJSON())));
360335

361-
HttpResponse<Buffer> resp = testRequest(webClient.post("/webauthn/callback")
362-
.putHeader(HttpHeaders.COOKIE.toString(), obtainedCookie[0])
336+
HttpResponse<Buffer> callbackResp = testRequest(webClient.post("/webauthn/callback")
337+
.putHeader(HttpHeaders.COOKIE.toString(), obtainedCookie)
363338
.sendBuffer(Buffer.buffer(request.encode())), 204, "No Content");
364-
String cookie = resp.getHeader(HttpHeaders.SET_COOKIE.toString());
365-
obtainedCookie[0] = extractVertxSessionCookie(cookie);
366-
367-
testStorage.find(username, null)
368-
.onSuccess(authenticators -> {
369-
assertNotNull(authenticators);
370-
assertEquals(1, authenticators.size());
371-
Authenticator authenticator = authenticators.get(0);
372-
// Check username, credid, counter, publicKey
373-
assertEquals(username, authenticator.getUsername());
374-
assertEquals(credId, authenticator.getCredID());
375-
assertEquals(2, authenticator.getCounter());
376-
assertEquals(publicKey, authenticator.getPublicKey());
377-
})
378-
.onFailure(x -> fail("Well that did not work"));
379-
380-
return obtainedCookie[0];
339+
obtainedCookie = extractVertxSessionCookie(callbackResp.getHeader(HttpHeaders.SET_COOKIE.toString()));
340+
341+
List<Authenticator> authenticators = testStorage.find(username, null).await();
342+
assertNotNull(authenticators);
343+
assertEquals(1, authenticators.size());
344+
Authenticator authenticator = authenticators.get(0);
345+
assertEquals(username, authenticator.getUsername());
346+
assertEquals(credId, authenticator.getCredID());
347+
assertEquals(2, authenticator.getCounter());
348+
assertEquals(publicKey, authenticator.getPublicKey());
349+
350+
return obtainedCookie;
381351
}
382352

383353
private AuthenticationRequest createAuthenticationRequest(ClientPlatform clientPlatform, String rpId, Challenge challenge, String username, String displayName) {
@@ -406,31 +376,4 @@ private AuthenticationRequest createAuthenticationRequest(ClientPlatform clientP
406376

407377
}
408378

409-
protected void testRequestBuffer(HttpMethod method, String path, Consumer<HttpClientRequest> requestAction, Consumer<HttpClientResponse> responseAction,
410-
int statusCode, String statusMessage,
411-
Consumer<Buffer> responseBodyBufferAction) throws Exception {
412-
RequestOptions requestOptions = new RequestOptions().setMethod(method).setPort(8080).setURI(path).setHost("localhost");
413-
Promise<Void> promise = Promise.promise();
414-
client.request(requestOptions).compose(req -> {
415-
if (requestAction != null) {
416-
requestAction.accept(req);
417-
}
418-
return req.send();
419-
}).onComplete(TestUtils.onSuccess(resp -> {
420-
assertEquals(statusCode, resp.statusCode());
421-
assertEquals(statusMessage, resp.statusMessage());
422-
if (responseAction != null) {
423-
responseAction.accept(resp);
424-
}
425-
if (responseBodyBufferAction == null) {
426-
promise.complete();
427-
} else {
428-
resp.bodyHandler(buff -> {
429-
responseBodyBufferAction.accept(buff);
430-
promise.complete();
431-
});
432-
}
433-
}));
434-
promise.future().await();
435-
}
436379
}

0 commit comments

Comments
 (0)