KEYCLOAK-12176 WebAuthn: show the attestation statement format in the admin console

This commit is contained in:
Takashi Norimatsu 2020-02-25 12:40:50 +09:00 committed by Marek Posolda
parent 1db87acc98
commit 8513760e25
6 changed files with 58 additions and 5 deletions

View file

@ -48,8 +48,8 @@ public class WebAuthnCredentialModel extends CredentialModel {
}
public static WebAuthnCredentialModel create(String credentialType, String userLabel, String aaguid, String credentialId,
String attestationStatement, String credentialPublicKey, long counter) {
WebAuthnCredentialData credentialData = new WebAuthnCredentialData(aaguid, credentialId, counter, attestationStatement, credentialPublicKey);
String attestationStatement, String credentialPublicKey, long counter, String attestationStatementFormat) {
WebAuthnCredentialData credentialData = new WebAuthnCredentialData(aaguid, credentialId, counter, attestationStatement, credentialPublicKey, attestationStatementFormat);
WebAuthnSecretData secretData = new WebAuthnSecretData();
WebAuthnCredentialModel credentialModel = new WebAuthnCredentialModel(credentialType, credentialData, secretData);

View file

@ -31,18 +31,21 @@ public class WebAuthnCredentialData {
private long counter;
private String attestationStatement;
private String credentialPublicKey;
private String attestationStatementFormat;
@JsonCreator
public WebAuthnCredentialData(@JsonProperty("aaguid") String aaguid,
@JsonProperty("credentialId") String credentialId,
@JsonProperty("counter") long counter,
@JsonProperty("attestationStatement") String attestationStatement,
@JsonProperty("credentialPublicKey") String credentialPublicKey ) {
@JsonProperty("credentialPublicKey") String credentialPublicKey,
@JsonProperty("attestationStatementFormat") String attestationStatementFormat) {
this.aaguid = aaguid;
this.credentialId = credentialId;
this.counter = counter;
this.attestationStatement = attestationStatement;
this.credentialPublicKey = credentialPublicKey;
this.attestationStatementFormat = attestationStatementFormat;
}
public String getAaguid() {
@ -69,6 +72,14 @@ public class WebAuthnCredentialData {
this.counter = counter;
}
public String getAttestationStatementFormat() {
return attestationStatementFormat;
}
public void setAttestationStatementFormat(String attestationStatementFormat) {
this.attestationStatementFormat = attestationStatementFormat;
}
@Override
public String toString() {
return "WebAuthnCredentialData { " +
@ -78,6 +89,7 @@ public class WebAuthnCredentialData {
", credentialPublicKey=" + credentialPublicKey +
", attestationStatement='" + attestationStatement + '\'' +
", credentialPublicKey='" + credentialPublicKey + '\'' +
", attestationStatementFormat='" + attestationStatementFormat + '\'' +
" }";
}
}

View file

@ -223,6 +223,7 @@ public class WebAuthnRegister implements RequiredActionProvider, CredentialRegis
credential.setAttestedCredentialData(registrationData.getAttestationObject().getAuthenticatorData().getAttestedCredentialData());
credential.setCount(registrationData.getAttestationObject().getAuthenticatorData().getSignCount());
credential.setAttestationStatementFormat(registrationData.getAttestationObject().getFormat());
// Save new webAuthn credential
WebAuthnCredentialProvider webAuthnCredProvider = (WebAuthnCredentialProvider) this.session.getProvider(CredentialProvider.class, getCredentialProviderId());

View file

@ -33,6 +33,7 @@ public class WebAuthnCredentialModelInput implements CredentialInput {
private long count;
private String credentialDBId;
private final String credentialType;
private String attestationStatementFormat;
public WebAuthnCredentialModelInput(String credentialType) {
this.credentialType = credentialType;
@ -106,16 +107,29 @@ public class WebAuthnCredentialModelInput implements CredentialInput {
return credentialType;
}
public String getAttestationStatementFormat() {
return attestationStatementFormat;
}
public void setAttestationStatementFormat(String attestationStatementFormat) {
this.attestationStatementFormat = attestationStatementFormat;
}
public String toString() {
StringBuilder sb = new StringBuilder("Credential Type = " + credentialType + ",");
if (credentialDBId != null)
sb.append("Credential DB Id = ")
.append(credentialDBId)
.append(",");
if (attestationStatement != null)
if (attestationStatement != null) {
sb.append("Attestation Statement Format = ")
.append(attestationStatement.getFormat())
.append(",");
} else if (attestationStatementFormat != null) {
sb.append("Attestation Statement Format = ")
.append(attestationStatementFormat)
.append(",");
}
if (attestedCredentialData != null) {
sb.append("AAGUID = ")
.append(attestedCredentialData.getAaguid().toString())

View file

@ -102,8 +102,9 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
String credentialId = Base64.encodeBytes(webAuthnModel.getAttestedCredentialData().getCredentialId());
String credentialPublicKey = credentialPublicKeyConverter.convertToDatabaseColumn(webAuthnModel.getAttestedCredentialData().getCOSEKey());
long counter = webAuthnModel.getCount();
String attestationStatementFormat = webAuthnModel.getAttestationStatementFormat();
WebAuthnCredentialModel model = WebAuthnCredentialModel.create(getType(), userLabel, aaguid, credentialId, null, credentialPublicKey, counter);
WebAuthnCredentialModel model = WebAuthnCredentialModel.create(getType(), userLabel, aaguid, credentialId, null, credentialPublicKey, counter, attestationStatementFormat);
model.setId(webAuthnModel.getCredentialDBId());
@ -141,6 +142,8 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
auth.setCredentialDBId(credential.getId());
auth.setAttestationStatementFormat(credData.getAttestationStatementFormat());
return auth;
}

View file

@ -31,6 +31,7 @@ import org.keycloak.common.util.RandomString;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.credential.WebAuthnCredentialModel;
import org.keycloak.models.credential.dto.WebAuthnCredentialData;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@ -47,12 +48,14 @@ import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.pages.webauthn.WebAuthnLoginPage;
import org.keycloak.testsuite.pages.webauthn.WebAuthnRegisterPage;
import org.keycloak.util.JsonSerialization;
import org.keycloak.testsuite.WebAuthnAssume;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@ -166,6 +169,7 @@ public class WebAuthnRegisterAndLoginTest extends AbstractTestRealmKeycloakTest
.assertEvent().getSessionId();
// confirm user registered
assertUserRegistered(userId, username.toLowerCase(), email.toLowerCase());
assertRegisteredCredentials(userId, ALL_ZERO_AAGUID, "none");
// logout by user
appPage.logout();
@ -304,10 +308,29 @@ public class WebAuthnRegisterAndLoginTest extends AbstractTestRealmKeycloakTest
assertEquals("lastName", user.getLastName());
}
private void assertRegisteredCredentials(String userId, String aaguid, String attestationStatementFormat) {
List<CredentialRepresentation> credentials = getCredentials(userId);
credentials.stream().forEach(i -> {
if (WebAuthnCredentialModel.TYPE_TWOFACTOR.equals(i.getType())) {
try {
WebAuthnCredentialData data = JsonSerialization.readValue(i.getCredentialData(), WebAuthnCredentialData.class);
assertEquals(aaguid, data.getAaguid());
assertEquals(attestationStatementFormat, data.getAttestationStatementFormat());
} catch (IOException e) {
Assert.fail();
}
}
});
}
protected UserRepresentation getUser(String userId) {
return testRealm().users().get(userId).toRepresentation();
}
protected List<CredentialRepresentation> getCredentials(String userId) {
return testRealm().users().get(userId).credentials();
}
private RealmRepresentation backupWebAuthnRealmSettings() {
RealmRepresentation rep = testRealm().toRepresentation();
signatureAlgorithms = rep.getWebAuthnPolicySignatureAlgorithms();