Skip to content

Commit

Permalink
Merge pull request #25174 from FroMage/webauthn-native-it
Browse files Browse the repository at this point in the history
Added WebAuthn IT and fixed native bug
  • Loading branch information
geoand authored Apr 27, 2022
2 parents 45154d6 + 7c1fe42 commit 7eddb34
Show file tree
Hide file tree
Showing 17 changed files with 1,044 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/native-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
{
"category": "Security3",
"timeout": 50,
"test-modules": "keycloak-authorization, smallrye-jwt-token-propagation",
"test-modules": "keycloak-authorization, smallrye-jwt-token-propagation, security-webauthn",
"os-name": "ubuntu-latest"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.security.webauthn.WebAuthnAuthenticationMechanism;
import io.quarkus.security.webauthn.WebAuthnAuthenticatorStorage;
import io.quarkus.security.webauthn.WebAuthnBuildTimeConfig;
Expand All @@ -23,6 +24,7 @@
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
import io.quarkus.vertx.http.deployment.VertxWebRouterBuildItem;
import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism;
import io.vertx.ext.auth.webauthn.impl.attestation.Attestation;

class QuarkusSecurityWebAuthnProcessor {

Expand Down Expand Up @@ -53,6 +55,11 @@ public void setup(
nonApplicationRootPathBuildItem.getNonApplicationRootPath());
}

@BuildStep(onlyIf = IsEnabled.class)
public ServiceProviderBuildItem serviceLoader() {
return ServiceProviderBuildItem.allProvidersFromClassPath(Attestation.class.getName());
}

@BuildStep(onlyIf = IsEnabled.class)
@Record(ExecutionTime.RUNTIME_INIT)
SyntheticBeanBuildItem initWebAuthnAuth(
Expand Down
1 change: 1 addition & 0 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
<module>elytron-resteasy</module>
<module>elytron-resteasy-reactive</module>
<module>elytron-undertow</module>
<module>security-webauthn</module>
<module>flyway</module>
<module>liquibase</module>
<module>liquibase-mongodb</module>
Expand Down
178 changes: 178 additions & 0 deletions integration-tests/security-webauthn/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-integration-tests-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-integration-test-security-webauthn</artifactId>
<name>Quarkus - Integration Tests - Security WebAuthn</name>
<description>The security-webauthn integration tests module</description>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-security-webauthn</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security-webauthn</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-security-webauthn-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>test-webauthn</id>
<activation>
<property>
<name>test-containers</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.it.security.webauthn;

import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/api/admin")
public class AdminResource {

@GET
@RolesAllowed("admin")
@Produces(MediaType.TEXT_PLAIN)
public String adminResource() {
return "admin";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package io.quarkus.it.security.webauthn;

import javax.inject.Inject;
import javax.ws.rs.BeanParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.jboss.resteasy.reactive.RestForm;

import io.quarkus.hibernate.reactive.panache.common.runtime.ReactiveTransactional;
import io.quarkus.security.webauthn.WebAuthnLoginResponse;
import io.quarkus.security.webauthn.WebAuthnRegisterResponse;
import io.quarkus.security.webauthn.WebAuthnSecurity;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.auth.webauthn.Authenticator;
import io.vertx.ext.web.RoutingContext;

@Path("")
public class LoginResource {

@Inject
WebAuthnSecurity webAuthnSecurity;

@Path("/login")
@POST
@ReactiveTransactional
public Uni<Response> login(@RestForm String userName,
@BeanParam WebAuthnLoginResponse webAuthnResponse,
RoutingContext ctx) {
// Input validation
if (userName == null || userName.isEmpty()
|| !webAuthnResponse.isSet()
|| !webAuthnResponse.isValid()) {
return Uni.createFrom().item(Response.status(Status.BAD_REQUEST).build());
}

Uni<User> userUni = User.findByUserName(userName);
return userUni.flatMap(user -> {
if (user == null) {
// Invalid user
return Uni.createFrom().item(Response.status(Status.BAD_REQUEST).build());
}
Uni<Authenticator> authenticator = this.webAuthnSecurity.login(webAuthnResponse, ctx);

return authenticator
// bump the auth counter
.invoke(auth -> user.webAuthnCredential.counter = auth.getCounter())
.map(auth -> {
// make a login cookie
this.webAuthnSecurity.rememberUser(auth.getUserName(), ctx);
return Response.ok().build();
})
// handle login failure
.onFailure().recoverWithItem(x -> {
// make a proper error response
return Response.status(Status.BAD_REQUEST).build();
});

});
}

@Path("/register")
@POST
@ReactiveTransactional
public Uni<Response> register(@RestForm String userName,
@BeanParam WebAuthnRegisterResponse webAuthnResponse,
RoutingContext ctx) {
// Input validation
if (userName == null || userName.isEmpty()
|| !webAuthnResponse.isSet()
|| !webAuthnResponse.isValid()) {
return Uni.createFrom().item(Response.status(Status.BAD_REQUEST).build());
}

Uni<User> userUni = User.findByUserName(userName);
return userUni.flatMap(user -> {
if (user != null) {
// Duplicate user
return Uni.createFrom().item(Response.status(Status.BAD_REQUEST).build());
}
Uni<Authenticator> authenticator = this.webAuthnSecurity.register(webAuthnResponse, ctx);

return authenticator
// store the user
.flatMap(auth -> {
User newUser = new User();
newUser.userName = auth.getUserName();
WebAuthnCredential credential = new WebAuthnCredential(auth, newUser);
return credential.persist()
.flatMap(c -> newUser.<User> persist());

})
.map(newUser -> {
// make a login cookie
this.webAuthnSecurity.rememberUser(newUser.userName, ctx);
return Response.ok().build();
})
// handle login failure
.onFailure().recoverWithItem(x -> {
// make a proper error response
return Response.status(Status.BAD_REQUEST).build();
});

});
}
}
Loading

0 comments on commit 7eddb34

Please sign in to comment.