KEYCLOAK-12176 WebAuthn: show the attestation statement format in the admin console
This commit is contained in:
parent
1db87acc98
commit
8513760e25
6 changed files with 58 additions and 5 deletions
|
@ -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);
|
||||
|
|
|
@ -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 + '\'' +
|
||||
" }";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue