Skip to content

Commit

Permalink
Merge pull request quarkusio#45132 from ynojima/revisit-options-chall…
Browse files Browse the repository at this point in the history
…enge-endpoints

Revisit options challenge endpoints
  • Loading branch information
FroMage authored Dec 17, 2024
2 parents d398488 + 2859983 commit e67b126
Show file tree
Hide file tree
Showing 33 changed files with 235 additions and 259 deletions.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/images/webauthn-custom-login.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/images/webauthn-custom-register.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/images/webauthn-login.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/images/webauthn-register.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
131 changes: 61 additions & 70 deletions docs/src/main/asciidoc/security-webauthn.adoc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Uni<String> register(@QueryParam("username") String username, @BeanParam
return security.register(username, register, ctx).map(authenticator -> {
// need to attach the authenticator to the user
userProvider.reallyStore(authenticator);
security.rememberUser(authenticator.getUserName(), ctx);
security.rememberUser(authenticator.getUsername(), ctx);
return "OK";
});
}
Expand All @@ -40,7 +40,7 @@ public Uni<String> login(@BeanParam WebAuthnLoginResponse login, RoutingContext
return security.login(login, ctx).map(authenticator -> {
// need to update the user's authenticator
userProvider.reallyUpdate(authenticator.getCredentialID(), authenticator.getCounter());
security.rememberUser(authenticator.getUserName(), ctx);
security.rememberUser(authenticator.getUsername(), ctx);
return "OK";
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class TestResource {
@Authenticated
@Path("secure")
@GET
public String getUserName() {
public String getUsername() {
return identity.getPrincipal().getName() + ": " + identity.getRoles();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public static void setupUsers() {
@Test
public void test() throws Exception {

Assertions.assertTrue(userProvider.findByUserName("stev").await().indefinitely().isEmpty());
Assertions.assertTrue(userProvider.findByUsername("stev").await().indefinitely().isEmpty());
CookieFilter cookieFilter = new CookieFilter();
String challenge = WebAuthnEndpointHelper.obtainRegistrationChallenge("stev", cookieFilter);
WebAuthnHardware hardwareKey = new WebAuthnHardware(url);
Expand All @@ -79,9 +79,9 @@ public void test() throws Exception {
.cookie("quarkus-credential", Matchers.notNullValue());

// make sure we stored the user
List<WebAuthnCredentialRecord> users = userProvider.findByUserName("stev").await().indefinitely();
List<WebAuthnCredentialRecord> users = userProvider.findByUsername("stev").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stev"));
Assertions.assertTrue(users.get(0).getUsername().equals("stev"));
Assertions.assertEquals(1, users.get(0).getCounter());

// make sure our login cookie works
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void test() throws Exception {
.given().redirects().follow(false)
.get("/cheese").then().statusCode(302);

Assertions.assertTrue(userProvider.findByUserName("stef").await().indefinitely().isEmpty());
Assertions.assertTrue(userProvider.findByUsername("stef").await().indefinitely().isEmpty());
CookieFilter cookieFilter = new CookieFilter();
WebAuthnHardware hardwareKey = new WebAuthnHardware(url);
String challenge = WebAuthnEndpointHelper.obtainRegistrationChallenge("stef", cookieFilter);
Expand All @@ -50,9 +50,9 @@ public void test() throws Exception {
WebAuthnEndpointHelper.invokeRegistration("stef", registration, cookieFilter);

// make sure we stored the user
List<WebAuthnCredentialRecord> users = userProvider.findByUserName("stef").await().indefinitely();
List<WebAuthnCredentialRecord> users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(1, users.get(0).getCounter());

// make sure our login cookie works
Expand All @@ -68,9 +68,9 @@ public void test() throws Exception {
WebAuthnEndpointHelper.invokeLogin(login, cookieFilter);

// make sure we bumped the user
users = userProvider.findByUserName("stef").await().indefinitely();
users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(2, users.get(0).getCounter());

// make sure our login cookie still works
Expand All @@ -86,9 +86,9 @@ public void test() throws Exception {
WebAuthnEndpointHelper.invokeLogin(login, cookieFilter);

// make sure we bumped the user
users = userProvider.findByUserName("stef").await().indefinitely();
users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(3, users.get(0).getCounter());

// make sure our login cookie still works
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public Uni<WebAuthnCredentialRecord> findByCredentialId(String credId) {
}

@Override
public Uni<List<WebAuthnCredentialRecord>> findByUserName(String userId) {
public Uni<List<WebAuthnCredentialRecord>> findByUsername(String userId) {
assertBlockingAllowed();
return super.findByUserName(userId);
return super.findByUsername(userId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void test() throws Exception {
.given().redirects().follow(false)
.get("/cheese").then().statusCode(302);

Assertions.assertTrue(userProvider.findByUserName("stef").await().indefinitely().isEmpty());
Assertions.assertTrue(userProvider.findByUsername("stef").await().indefinitely().isEmpty());
CookieFilter cookieFilter = new CookieFilter();
String challenge = WebAuthnEndpointHelper.obtainRegistrationChallenge("stef", cookieFilter);
WebAuthnHardware hardwareKey = new WebAuthnHardware(url);
Expand All @@ -77,9 +77,9 @@ public void test() throws Exception {
.cookie("main-cookie", Matchers.notNullValue());

// make sure we stored the user
List<WebAuthnCredentialRecord> users = userProvider.findByUserName("stef").await().indefinitely();
List<WebAuthnCredentialRecord> users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(1, users.get(0).getCounter());

// make sure our login cookie works
Expand All @@ -104,9 +104,9 @@ public void test() throws Exception {
.cookie("main-cookie", Matchers.notNullValue());

// make sure we bumped the user
users = userProvider.findByUserName("stef").await().indefinitely();
users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(2, users.get(0).getCounter());

// make sure our login cookie still works
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void test() throws Exception {
.given().redirects().follow(false)
.get("/cheese").then().statusCode(302);

Assertions.assertTrue(userProvider.findByUserName("stef").await().indefinitely().isEmpty());
Assertions.assertTrue(userProvider.findByUsername("stef").await().indefinitely().isEmpty());
CookieFilter cookieFilter = new CookieFilter();
String challenge = WebAuthnEndpointHelper.obtainRegistrationChallenge("stef", cookieFilter);
WebAuthnHardware hardwareKey = new WebAuthnHardware(url);
Expand All @@ -78,9 +78,9 @@ public void test() throws Exception {
.cookie("quarkus-credential", Matchers.notNullValue());

// make sure we stored the user
List<WebAuthnCredentialRecord> users = userProvider.findByUserName("stef").await().indefinitely();
List<WebAuthnCredentialRecord> users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(1, users.get(0).getCounter());

// make sure our login cookie works
Expand All @@ -107,9 +107,9 @@ public void test() throws Exception {
.cookie("quarkus-credential", Matchers.notNullValue());

// make sure we bumped the user
users = userProvider.findByUserName("stef").await().indefinitely();
users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(2, users.get(0).getCounter());

// make sure our login cookie still works
Expand All @@ -127,15 +127,15 @@ public void test() throws Exception {
() -> WebAuthnEndpointHelper.invokeLogin(defaultLogin, finalCookieFilter));

// make sure we did not bump the user
users = userProvider.findByUserName("stef").await().indefinitely();
users = userProvider.findByUsername("stef").await().indefinitely();
Assertions.assertEquals(1, users.size());
Assertions.assertTrue(users.get(0).getUserName().equals("stef"));
Assertions.assertTrue(users.get(0).getUsername().equals("stef"));
Assertions.assertEquals(2, users.get(0).getCounter());
}

@Test
public void checkDefaultRegistrationDisabled() {
Assertions.assertTrue(userProvider.findByUserName("stef").await().indefinitely().isEmpty());
Assertions.assertTrue(userProvider.findByUsername("stef").await().indefinitely().isEmpty());
CookieFilter cookieFilter = new CookieFilter();
WebAuthnHardware hardwareKey = new WebAuthnHardware(url);
String challenge = WebAuthnEndpointHelper.obtainRegistrationChallenge("stef", cookieFilter);
Expand All @@ -146,7 +146,7 @@ public void checkDefaultRegistrationDisabled() {
() -> WebAuthnEndpointHelper.invokeRegistration("stef", registration, cookieFilter));

// make sure we did not create any user
Assertions.assertTrue(userProvider.findByUserName("stef").await().indefinitely().isEmpty());
Assertions.assertTrue(userProvider.findByUsername("stef").await().indefinitely().isEmpty());
}

private void checkLoggedIn(CookieFilter cookieFilter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public Uni<WebAuthnCredentialRecord> findByCredentialId(String credId) {
}

@Override
public Uni<List<WebAuthnCredentialRecord>> findByUserName(String userId) {
public Uni<List<WebAuthnCredentialRecord>> findByUsername(String userId) {
assertRequestContext();
return super.findByUserName(userId);
return super.findByUsername(userId);
}

private void assertRequestContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public Uni<WebAuthnCredentialRecord> findByCredentialId(String credId) {
}

@Override
public Uni<List<WebAuthnCredentialRecord>> findByUserName(String userId) {
public Uni<List<WebAuthnCredentialRecord>> findByUsername(String userId) {
assertBlockingNotAllowed();
return super.findByUserName(userId);
return super.findByUsername(userId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.quarkus.test.security.webauthn.WebAuthnTestUserProvider;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.vertx.core.json.JsonObject;

public class WebAuthnOriginsTest {

Expand All @@ -24,10 +23,9 @@ public class WebAuthnOriginsTest {
public void testLoginRpFromFirstOrigin() {
RestAssured
.given()
.body(new JsonObject()
.put("name", "foo").encode())
.contentType(ContentType.JSON)
.post("/q/webauthn/register-options-challenge")
.contentType(ContentType.URLENC)
.queryParam("username", "foo")
.get("/q/webauthn/register-options-challenge")
.then()
.log().all()
.statusCode(200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import io.restassured.RestAssured;
import io.restassured.filter.cookie.CookieFilter;
import io.restassured.http.ContentType;
import io.vertx.core.json.JsonObject;

public class WebAuthnTest {

Expand All @@ -31,10 +30,9 @@ public void testJavaScriptFile() {
public void testLoginRpFromFirstOrigin() {
RestAssured
.given()
.body(new JsonObject()
.put("name", "foo").encode())
.contentType(ContentType.JSON)
.post("/q/webauthn/register-options-challenge")
.queryParam("username", "foo")
.get("/q/webauthn/register-options-challenge")
.then()
.statusCode(200)
.contentType(ContentType.JSON)
Expand All @@ -48,19 +46,17 @@ public void testRegisterChallengeIsEqualAcrossCalls() {
String challenge = RestAssured
.given()
.filter(cookieFilter)
.body(new JsonObject()
.put("name", "foo").encode())
.contentType(ContentType.JSON)
.post("/q/webauthn/register-options-challenge")
.contentType(ContentType.URLENC)
.queryParam("username", "foo")
.get("/q/webauthn/register-options-challenge")
.jsonPath().get("challenge");

RestAssured
.given()
.filter(cookieFilter)
.body(new JsonObject()
.put("name", "foo").encode())
.contentType(ContentType.JSON)
.post("/q/webauthn/register-options-challenge")
.contentType(ContentType.URLENC)
.queryParam("username", "foo")
.get("/q/webauthn/register-options-challenge")
.then()
.statusCode(200)
.contentType(ContentType.JSON)
Expand All @@ -74,17 +70,15 @@ public void testLoginChallengeIsEqualAcrossCalls() {
String challenge = RestAssured
.given()
.filter(cookieFilter)
.body(new JsonObject().encode())
.contentType(ContentType.JSON)
.post("/q/webauthn/login-options-challenge")
.contentType(ContentType.URLENC)
.get("/q/webauthn/login-options-challenge")
.jsonPath().get("challenge");

RestAssured
.given()
.filter(cookieFilter)
.body(new JsonObject().encode())
.contentType(ContentType.JSON)
.post("/q/webauthn/login-options-challenge")
.contentType(ContentType.URLENC)
.get("/q/webauthn/login-options-challenge")
.then()
.statusCode(200)
.contentType(ContentType.JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class WebAuthnAuthenticatorStorage {
@Inject
Vertx vertx;

public Uni<List<WebAuthnCredentialRecord>> findByUserName(String userName) {
return runPotentiallyBlocking(() -> userProvider.findByUserName(userName));
public Uni<List<WebAuthnCredentialRecord>> findByUsername(String username) {
return runPotentiallyBlocking(() -> userProvider.findByUsername(username));
}

public Uni<WebAuthnCredentialRecord> findByCredID(String credID) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,9 @@ public void wellKnown(RoutingContext ctx) {
*/
public void registerOptionsChallenge(RoutingContext ctx) {
try {
// might throw runtime exception if there's no json or is bad formed
final JsonObject webauthnRegister = ctx.getBodyAsJson();

String name = webauthnRegister.getString("name");
String displayName = webauthnRegister.getString("displayName");
withContext(() -> security.getRegisterChallenge(name, displayName, ctx))
String username = ctx.queryParams().get("username");
String displayName = ctx.queryParams().get("displayName");
withContext(() -> security.getRegisterChallenge(username, displayName, ctx))
.map(challenge -> security.toJsonString(challenge))
.subscribe().with(challenge -> ok(ctx, challenge), ctx::fail);

Expand All @@ -77,11 +74,8 @@ private <T> Uni<T> withContext(Supplier<Uni<T>> uni) {
*/
public void loginOptionsChallenge(RoutingContext ctx) {
try {
// might throw runtime exception if there's no json or is bad formed
final JsonObject webauthnLogin = ctx.getBodyAsJson();

String name = webauthnLogin.getString("name");
withContext(() -> security.getLoginChallenge(name, ctx))
String username = ctx.queryParams().get("username");
withContext(() -> security.getLoginChallenge(username, ctx))
.map(challenge -> security.toJsonString(challenge))
.subscribe().with(challenge -> ok(ctx, challenge), ctx::fail);

Expand All @@ -106,7 +100,7 @@ public void login(RoutingContext ctx) {
withContext(() -> security.login(webauthnResp, ctx))
.onItem().call(record -> security.storage().update(record.getCredentialID(), record.getCounter()))
.subscribe().with(record -> {
security.rememberUser(record.getUserName(), ctx);
security.rememberUser(record.getUsername(), ctx);
ok(ctx);
}, x -> ctx.fail(400, x));
} catch (IllegalArgumentException e) {
Expand All @@ -131,7 +125,7 @@ public void register(RoutingContext ctx) {
withContext(() -> security.register(username, webauthnResp, ctx))
.onItem().call(record -> security.storage().create(record))
.subscribe().with(record -> {
security.rememberUser(record.getUserName(), ctx);
security.rememberUser(record.getUsername(), ctx);
ok(ctx);
}, x -> ctx.fail(400, x));
} catch (IllegalArgumentException e) {
Expand Down
Loading

0 comments on commit e67b126

Please sign in to comment.