Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revisit options challenge endpoints #45132

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading