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,
|
public static WebAuthnCredentialModel create(String credentialType, String userLabel, String aaguid, String credentialId,
|
||||||
String attestationStatement, String credentialPublicKey, long counter) {
|
String attestationStatement, String credentialPublicKey, long counter, String attestationStatementFormat) {
|
||||||
WebAuthnCredentialData credentialData = new WebAuthnCredentialData(aaguid, credentialId, counter, attestationStatement, credentialPublicKey);
|
WebAuthnCredentialData credentialData = new WebAuthnCredentialData(aaguid, credentialId, counter, attestationStatement, credentialPublicKey, attestationStatementFormat);
|
||||||
WebAuthnSecretData secretData = new WebAuthnSecretData();
|
WebAuthnSecretData secretData = new WebAuthnSecretData();
|
||||||
|
|
||||||
WebAuthnCredentialModel credentialModel = new WebAuthnCredentialModel(credentialType, credentialData, secretData);
|
WebAuthnCredentialModel credentialModel = new WebAuthnCredentialModel(credentialType, credentialData, secretData);
|
||||||
|
|
|
@ -31,18 +31,21 @@ public class WebAuthnCredentialData {
|
||||||
private long counter;
|
private long counter;
|
||||||
private String attestationStatement;
|
private String attestationStatement;
|
||||||
private String credentialPublicKey;
|
private String credentialPublicKey;
|
||||||
|
private String attestationStatementFormat;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public WebAuthnCredentialData(@JsonProperty("aaguid") String aaguid,
|
public WebAuthnCredentialData(@JsonProperty("aaguid") String aaguid,
|
||||||
@JsonProperty("credentialId") String credentialId,
|
@JsonProperty("credentialId") String credentialId,
|
||||||
@JsonProperty("counter") long counter,
|
@JsonProperty("counter") long counter,
|
||||||
@JsonProperty("attestationStatement") String attestationStatement,
|
@JsonProperty("attestationStatement") String attestationStatement,
|
||||||
@JsonProperty("credentialPublicKey") String credentialPublicKey ) {
|
@JsonProperty("credentialPublicKey") String credentialPublicKey,
|
||||||
|
@JsonProperty("attestationStatementFormat") String attestationStatementFormat) {
|
||||||
this.aaguid = aaguid;
|
this.aaguid = aaguid;
|
||||||
this.credentialId = credentialId;
|
this.credentialId = credentialId;
|
||||||
this.counter = counter;
|
this.counter = counter;
|
||||||
this.attestationStatement = attestationStatement;
|
this.attestationStatement = attestationStatement;
|
||||||
this.credentialPublicKey = credentialPublicKey;
|
this.credentialPublicKey = credentialPublicKey;
|
||||||
|
this.attestationStatementFormat = attestationStatementFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAaguid() {
|
public String getAaguid() {
|
||||||
|
@ -69,6 +72,14 @@ public class WebAuthnCredentialData {
|
||||||
this.counter = counter;
|
this.counter = counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAttestationStatementFormat() {
|
||||||
|
return attestationStatementFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttestationStatementFormat(String attestationStatementFormat) {
|
||||||
|
this.attestationStatementFormat = attestationStatementFormat;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "WebAuthnCredentialData { " +
|
return "WebAuthnCredentialData { " +
|
||||||
|
@ -78,6 +89,7 @@ public class WebAuthnCredentialData {
|
||||||
", credentialPublicKey=" + credentialPublicKey +
|
", credentialPublicKey=" + credentialPublicKey +
|
||||||
", attestationStatement='" + attestationStatement + '\'' +
|
", attestationStatement='" + attestationStatement + '\'' +
|
||||||
", credentialPublicKey='" + credentialPublicKey + '\'' +
|
", credentialPublicKey='" + credentialPublicKey + '\'' +
|
||||||
|
", attestationStatementFormat='" + attestationStatementFormat + '\'' +
|
||||||
" }";
|
" }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,7 @@ public class WebAuthnRegister implements RequiredActionProvider, CredentialRegis
|
||||||
|
|
||||||
credential.setAttestedCredentialData(registrationData.getAttestationObject().getAuthenticatorData().getAttestedCredentialData());
|
credential.setAttestedCredentialData(registrationData.getAttestationObject().getAuthenticatorData().getAttestedCredentialData());
|
||||||
credential.setCount(registrationData.getAttestationObject().getAuthenticatorData().getSignCount());
|
credential.setCount(registrationData.getAttestationObject().getAuthenticatorData().getSignCount());
|
||||||
|
credential.setAttestationStatementFormat(registrationData.getAttestationObject().getFormat());
|
||||||
|
|
||||||
// Save new webAuthn credential
|
// Save new webAuthn credential
|
||||||
WebAuthnCredentialProvider webAuthnCredProvider = (WebAuthnCredentialProvider) this.session.getProvider(CredentialProvider.class, getCredentialProviderId());
|
WebAuthnCredentialProvider webAuthnCredProvider = (WebAuthnCredentialProvider) this.session.getProvider(CredentialProvider.class, getCredentialProviderId());
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class WebAuthnCredentialModelInput implements CredentialInput {
|
||||||
private long count;
|
private long count;
|
||||||
private String credentialDBId;
|
private String credentialDBId;
|
||||||
private final String credentialType;
|
private final String credentialType;
|
||||||
|
private String attestationStatementFormat;
|
||||||
|
|
||||||
public WebAuthnCredentialModelInput(String credentialType) {
|
public WebAuthnCredentialModelInput(String credentialType) {
|
||||||
this.credentialType = credentialType;
|
this.credentialType = credentialType;
|
||||||
|
@ -106,16 +107,29 @@ public class WebAuthnCredentialModelInput implements CredentialInput {
|
||||||
return credentialType;
|
return credentialType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAttestationStatementFormat() {
|
||||||
|
return attestationStatementFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttestationStatementFormat(String attestationStatementFormat) {
|
||||||
|
this.attestationStatementFormat = attestationStatementFormat;
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder("Credential Type = " + credentialType + ",");
|
StringBuilder sb = new StringBuilder("Credential Type = " + credentialType + ",");
|
||||||
if (credentialDBId != null)
|
if (credentialDBId != null)
|
||||||
sb.append("Credential DB Id = ")
|
sb.append("Credential DB Id = ")
|
||||||
.append(credentialDBId)
|
.append(credentialDBId)
|
||||||
.append(",");
|
.append(",");
|
||||||
if (attestationStatement != null)
|
if (attestationStatement != null) {
|
||||||
sb.append("Attestation Statement Format = ")
|
sb.append("Attestation Statement Format = ")
|
||||||
.append(attestationStatement.getFormat())
|
.append(attestationStatement.getFormat())
|
||||||
.append(",");
|
.append(",");
|
||||||
|
} else if (attestationStatementFormat != null) {
|
||||||
|
sb.append("Attestation Statement Format = ")
|
||||||
|
.append(attestationStatementFormat)
|
||||||
|
.append(",");
|
||||||
|
}
|
||||||
if (attestedCredentialData != null) {
|
if (attestedCredentialData != null) {
|
||||||
sb.append("AAGUID = ")
|
sb.append("AAGUID = ")
|
||||||
.append(attestedCredentialData.getAaguid().toString())
|
.append(attestedCredentialData.getAaguid().toString())
|
||||||
|
|
|
@ -102,8 +102,9 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
|
||||||
String credentialId = Base64.encodeBytes(webAuthnModel.getAttestedCredentialData().getCredentialId());
|
String credentialId = Base64.encodeBytes(webAuthnModel.getAttestedCredentialData().getCredentialId());
|
||||||
String credentialPublicKey = credentialPublicKeyConverter.convertToDatabaseColumn(webAuthnModel.getAttestedCredentialData().getCOSEKey());
|
String credentialPublicKey = credentialPublicKeyConverter.convertToDatabaseColumn(webAuthnModel.getAttestedCredentialData().getCOSEKey());
|
||||||
long counter = webAuthnModel.getCount();
|
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());
|
model.setId(webAuthnModel.getCredentialDBId());
|
||||||
|
|
||||||
|
@ -141,6 +142,8 @@ public class WebAuthnCredentialProvider implements CredentialProvider<WebAuthnCr
|
||||||
|
|
||||||
auth.setCredentialDBId(credential.getId());
|
auth.setCredentialDBId(credential.getId());
|
||||||
|
|
||||||
|
auth.setAttestationStatementFormat(credData.getAttestationStatementFormat());
|
||||||
|
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.keycloak.common.util.RandomString;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.credential.WebAuthnCredentialModel;
|
import org.keycloak.models.credential.WebAuthnCredentialModel;
|
||||||
|
import org.keycloak.models.credential.dto.WebAuthnCredentialData;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.EventRepresentation;
|
import org.keycloak.representations.idm.EventRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
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.RegisterPage;
|
||||||
import org.keycloak.testsuite.pages.webauthn.WebAuthnLoginPage;
|
import org.keycloak.testsuite.pages.webauthn.WebAuthnLoginPage;
|
||||||
import org.keycloak.testsuite.pages.webauthn.WebAuthnRegisterPage;
|
import org.keycloak.testsuite.pages.webauthn.WebAuthnRegisterPage;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.testsuite.WebAuthnAssume;
|
import org.keycloak.testsuite.WebAuthnAssume;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -166,6 +169,7 @@ public class WebAuthnRegisterAndLoginTest extends AbstractTestRealmKeycloakTest
|
||||||
.assertEvent().getSessionId();
|
.assertEvent().getSessionId();
|
||||||
// confirm user registered
|
// confirm user registered
|
||||||
assertUserRegistered(userId, username.toLowerCase(), email.toLowerCase());
|
assertUserRegistered(userId, username.toLowerCase(), email.toLowerCase());
|
||||||
|
assertRegisteredCredentials(userId, ALL_ZERO_AAGUID, "none");
|
||||||
|
|
||||||
// logout by user
|
// logout by user
|
||||||
appPage.logout();
|
appPage.logout();
|
||||||
|
@ -304,10 +308,29 @@ public class WebAuthnRegisterAndLoginTest extends AbstractTestRealmKeycloakTest
|
||||||
assertEquals("lastName", user.getLastName());
|
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) {
|
protected UserRepresentation getUser(String userId) {
|
||||||
return testRealm().users().get(userId).toRepresentation();
|
return testRealm().users().get(userId).toRepresentation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected List<CredentialRepresentation> getCredentials(String userId) {
|
||||||
|
return testRealm().users().get(userId).credentials();
|
||||||
|
}
|
||||||
|
|
||||||
private RealmRepresentation backupWebAuthnRealmSettings() {
|
private RealmRepresentation backupWebAuthnRealmSettings() {
|
||||||
RealmRepresentation rep = testRealm().toRepresentation();
|
RealmRepresentation rep = testRealm().toRepresentation();
|
||||||
signatureAlgorithms = rep.getWebAuthnPolicySignatureAlgorithms();
|
signatureAlgorithms = rep.getWebAuthnPolicySignatureAlgorithms();
|
||||||
|
|
Loading…
Reference in a new issue