Skip to content

Commit

Permalink
Update test suite for webauthn4j changes
Browse files Browse the repository at this point in the history
  • Loading branch information
FroMage committed Dec 13, 2024
1 parent a54dbd6 commit 604172c
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 331 deletions.
3 changes: 1 addition & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,7 @@
<module>security/keycloak-oidc-client-reactive-extended</module>
<module>security/vertx-jwt</module>
<module>security/oidc-client-mutual-tls</module>
<!-- FIXME: mvavrik deal with this ASAP! -->
<!-- <module>security/webauthn</module> -->
<module>security/webauthn</module>
</modules>
</profile>
<profile>
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,73 +1,38 @@
package io.quarkus.ts.security.webauthn.model;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import jakarta.persistence.Entity;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;

import io.quarkus.hibernate.reactive.panache.PanacheEntity;
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
import io.quarkus.security.webauthn.WebAuthnCredentialRecord;
import io.quarkus.security.webauthn.WebAuthnCredentialRecord.RequiredPersistedData;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.auth.webauthn.Authenticator;
import io.vertx.ext.auth.webauthn.PublicKeyCredential;

@Table(uniqueConstraints = @UniqueConstraint(columnNames = { "userName", "credID" }))
@Entity
public class WebAuthnCredential extends PanacheEntity {
/**
* The username linked to this authenticator
*/
public String userName;

/**
* The type of key (must be "public-key")
*/
public String type = "public-key";

public class WebAuthnCredential extends PanacheEntityBase {
/**
* The non user identifiable id for the authenticator
*/
@Id
public String credID;

/**
* The public key associated with this authenticator
*/
public String publicKey;
public byte[] publicKey;

public long publicKeyAlgorithm;

/**
* The signature counter of the authenticator to prevent replay attacks
*/
public long counter;

public String aaguid;

/**
* The Authenticator attestation certificates object, a JSON like:
*
* <pre>{@code
* {
* "alg": "string",
* "x5c": [
* "base64"
* ]
* }
* }</pre>
*/
/**
* The algorithm used for the public credential
*/
public PublicKeyCredential alg;

/**
* The list of X509 certificates encoded as base64url.
*/
@OneToMany(mappedBy = "webAuthnCredential")
public List<WebAuthnCertificate> webAuthnx509Certificates = new ArrayList<>();

public String fmt;
public UUID aaguid;

// owning side
@OneToOne
Expand All @@ -76,43 +41,29 @@ public class WebAuthnCredential extends PanacheEntity {
public WebAuthnCredential() {
}

public WebAuthnCredential(Authenticator authenticator, User user) {
aaguid = authenticator.getAaguid();
if (authenticator.getAttestationCertificates() != null)
alg = authenticator.getAttestationCertificates().getAlg();
counter = authenticator.getCounter();
credID = authenticator.getCredID();
fmt = authenticator.getFmt();
publicKey = authenticator.getPublicKey();
type = authenticator.getType();
userName = authenticator.getUserName();
if (authenticator.getAttestationCertificates() != null
&& authenticator.getAttestationCertificates().getX5c() != null) {
for (String x509VCertificate : authenticator.getAttestationCertificates().getX5c()) {
WebAuthnCertificate cert = new WebAuthnCertificate();
cert.base64X509Certificate = x509VCertificate;
cert.webAuthnCredential = this;
this.webAuthnx509Certificates.add(cert);
}
}
public WebAuthnCredential(WebAuthnCredentialRecord credentialRecord, User user) {
RequiredPersistedData requiredPersistedData = credentialRecord.getRequiredPersistedData();
aaguid = requiredPersistedData.aaguid();
counter = requiredPersistedData.counter();
credID = requiredPersistedData.credentialId();
publicKey = requiredPersistedData.publicKey();
publicKeyAlgorithm = requiredPersistedData.publicKeyAlgorithm();
this.user = user;
user.webAuthnCredential = this;
}

public static Uni<WebAuthnCredential> createWebAuthnCredential(Authenticator authenticator, User user) {
WebAuthnCredential credential = new WebAuthnCredential(authenticator, user);
credential.persistAndFlush();
user.webAuthnCredential = credential;
user.persistAndFlush();
return Uni.createFrom().item(credential);
public WebAuthnCredentialRecord toWebAuthnCredentialRecord() {
return WebAuthnCredentialRecord
.fromRequiredPersistedData(
new RequiredPersistedData(user.userName, credID, aaguid, publicKey, publicKeyAlgorithm, counter));
}

public static Uni<List<WebAuthnCredential>> findByUserName(String userName) {
return list("userName", userName);
return list("user.userName", userName);
}

public static Uni<List<WebAuthnCredential>> findByCredID(String credID) {
return list("credID", credID);
public static Uni<WebAuthnCredential> findByCredentialId(String credID) {
return findById(credID);
}

public <T> Uni<T> fetch(T association) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.quarkus.ts.security.webauthn.security;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand All @@ -9,79 +8,46 @@
import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
import io.quarkus.security.webauthn.WebAuthnCredentialRecord;
import io.quarkus.security.webauthn.WebAuthnUserProvider;
import io.quarkus.ts.security.webauthn.model.User;
import io.quarkus.ts.security.webauthn.model.WebAuthnCredential;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.auth.webauthn.AttestationCertificates;
import io.vertx.ext.auth.webauthn.Authenticator;

@ApplicationScoped
public class MyWebAuthnSetup implements WebAuthnUserProvider {

@WithTransaction
@Override
public Uni<List<Authenticator>> findWebAuthnCredentialsByUserName(String userName) {
public Uni<List<WebAuthnCredentialRecord>> findByUserName(String userName) {
return WebAuthnCredential.findByUserName(userName)
.flatMap(MyWebAuthnSetup::toAuthenticators);
.map(list -> list.stream().map(WebAuthnCredential::toWebAuthnCredentialRecord).toList());
}

@WithTransaction
@Override
public Uni<List<Authenticator>> findWebAuthnCredentialsByCredID(String credID) {
return WebAuthnCredential.findByCredID(credID)
.flatMap(MyWebAuthnSetup::toAuthenticators);
public Uni<WebAuthnCredentialRecord> findByCredentialId(String credentialId) {
return WebAuthnCredential.findByCredentialId(credentialId)
.onItem().ifNull().failWith(() -> new RuntimeException("No such credentials"))
.map(WebAuthnCredential::toWebAuthnCredentialRecord);
}

@WithTransaction
@Override
public Uni<Void> updateOrStoreWebAuthnCredentials(Authenticator authenticator) {
return User.findByUserName(authenticator.getUserName())
.flatMap(user -> {
// new user
if (user == null) {
User newUser = new User();
newUser.userName = authenticator.getUserName();
WebAuthnCredential credential = new WebAuthnCredential(authenticator, newUser);
return credential.persist()
.flatMap(c -> newUser.persist())
.onItem().ignore().andContinueWithNull();
} else {

// existing user
user.webAuthnCredential.counter = authenticator.getCounter();
return Uni.createFrom().nullItem();
}
});
}

private static Uni<List<Authenticator>> toAuthenticators(List<WebAuthnCredential> dbs) {
// can't call combine/uni on empty list
if (dbs.isEmpty())
return Uni.createFrom().item(Collections.emptyList());
List<Uni<Authenticator>> ret = new ArrayList<>(dbs.size());
for (WebAuthnCredential db : dbs) {
ret.add(toAuthenticator(db));
}
return Uni.combine().all().unis(ret).with(f -> (List) f);
public Uni<Void> store(WebAuthnCredentialRecord credentialRecord) {
User newUser = new User();
newUser.userName = credentialRecord.getUserName();
WebAuthnCredential credential = new WebAuthnCredential(credentialRecord, newUser);
return credential.persist()
.flatMap(c -> newUser.persist())
.onItem().ignore().andContinueWithNull();
}

private static Uni<Authenticator> toAuthenticator(WebAuthnCredential credential) {
return credential.fetch(credential.webAuthnx509Certificates)
.map(x5c -> {
Authenticator ret = new Authenticator();
ret.setAaguid(credential.aaguid);
AttestationCertificates attestationCertificates = new AttestationCertificates();
attestationCertificates.setAlg(credential.alg);
ret.setAttestationCertificates(attestationCertificates);
ret.setCounter(credential.counter);
ret.setCredID(credential.credID);
ret.setFmt(credential.fmt);
ret.setPublicKey(credential.publicKey);
ret.setType(credential.type);
ret.setUserName(credential.userName);
return ret;
});
@WithTransaction
@Override
public Uni<Void> update(String credentialId, long counter) {
return WebAuthnCredential.findByCredentialId(credentialId)
.onItem().ignore().andContinueWithNull();
}

@Override
Expand Down
3 changes: 1 addition & 2 deletions security/webauthn/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
quarkus.hibernate-orm.database.generation=drop-and-create


quarkus.webauthn.enable-registration-endpoint=true
Loading

0 comments on commit 604172c

Please sign in to comment.