Remove java.util.Date from VerifiableCredential (#30920)

closes #30918

Signed-off-by: Pascal Knüppel <pascal.knueppel@governikus.de>
Signed-off-by: Captain-P-Goldfish <captain.p.goldfish@gmx.de>
This commit is contained in:
Pascal Knüppel 2024-07-18 09:52:02 +02:00 committed by GitHub
parent 526286e851
commit 018a0802bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 54 additions and 60 deletions

View file

@ -54,6 +54,10 @@
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId> <artifactId>jackson-datatype-jdk8</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.logging</groupId> <groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId> <artifactId>jboss-logging</artifactId>

View file

@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -43,6 +44,7 @@ public class JsonSerialization {
static { static {
mapper.registerModule(new Jdk8Module()); mapper.registerModule(new Jdk8Module());
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
prettyMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); prettyMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

View file

@ -462,7 +462,7 @@ public class OID4VCIssuerEndpoint {
// set the required claims // set the required claims
VerifiableCredential vc = new VerifiableCredential() VerifiableCredential vc = new VerifiableCredential()
.setIssuer(URI.create(issuerDid)) .setIssuer(URI.create(issuerDid))
.setIssuanceDate(Date.from(Instant.ofEpochMilli(timeProvider.currentTimeMillis()))) .setIssuanceDate(Instant.ofEpochMilli(timeProvider.currentTimeMillis()))
.setType(List.of(vcType)); .setType(List.of(vcType));
Map<String, Object> subjectClaims = new HashMap<>(); Map<String, Object> subjectClaims = new HashMap<>();

View file

@ -28,6 +28,7 @@ import org.keycloak.protocol.oid4vc.model.VerifiableCredential;
import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.JsonWebToken;
import java.net.URI; import java.net.URI;
import java.time.Instant;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -74,7 +75,7 @@ public class JwtSigningService extends SigningService<String> {
// Get the issuance date from the credential. Since nbf is mandatory, we set it to the current time if not // Get the issuance date from the credential. Since nbf is mandatory, we set it to the current time if not
// provided // provided
long iat = Optional.ofNullable(verifiableCredential.getIssuanceDate()) long iat = Optional.ofNullable(verifiableCredential.getIssuanceDate())
.map(issuanceDate -> issuanceDate.toInstant().getEpochSecond()) .map(Instant::getEpochSecond)
.orElse((long) timeProvider.currentTimeSeconds()); .orElse((long) timeProvider.currentTimeSeconds());
// set mandatory fields // set mandatory fields
@ -86,7 +87,7 @@ public class JwtSigningService extends SigningService<String> {
// expiry is optional // expiry is optional
Optional.ofNullable(verifiableCredential.getExpirationDate()) Optional.ofNullable(verifiableCredential.getExpirationDate())
.ifPresent(d -> jsonWebToken.exp(d.toInstant().getEpochSecond())); .ifPresent(d -> jsonWebToken.exp(d.getEpochSecond()));
// subject id should only be set if the credential subject has an id. // subject id should only be set if the credential subject has an id.
Optional.ofNullable( Optional.ofNullable(

View file

@ -18,7 +18,6 @@
package org.keycloak.protocol.oid4vc.issuance.signing; package org.keycloak.protocol.oid4vc.issuance.signing;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.keycloak.common.util.Base64; import org.keycloak.common.util.Base64;
import org.keycloak.crypto.KeyWrapper; import org.keycloak.crypto.KeyWrapper;
import org.keycloak.crypto.SignatureProvider; import org.keycloak.crypto.SignatureProvider;
@ -48,7 +47,7 @@ public class LDSigningService extends SigningService<VerifiableCredential> {
private final TimeProvider timeProvider; private final TimeProvider timeProvider;
private final String keyId; private final String keyId;
public LDSigningService(KeycloakSession keycloakSession, String keyId, String algorithmType, String ldpType, ObjectMapper objectMapper, TimeProvider timeProvider, Optional<String> kid) { public LDSigningService(KeycloakSession keycloakSession, String keyId, String algorithmType, String ldpType, TimeProvider timeProvider, Optional<String> kid) {
super(keycloakSession, keyId, algorithmType); super(keycloakSession, keyId, algorithmType);
this.timeProvider = timeProvider; this.timeProvider = timeProvider;
this.keyId = kid.orElse(keyId); this.keyId = kid.orElse(keyId);
@ -66,7 +65,7 @@ public class LDSigningService extends SigningService<VerifiableCredential> {
linkedDataCryptographicSuite = switch (ldpType) { linkedDataCryptographicSuite = switch (ldpType) {
case Ed255192018Suite.PROOF_TYPE -> case Ed255192018Suite.PROOF_TYPE ->
new Ed255192018Suite(objectMapper, signatureProvider.signer(signingKey)); new Ed255192018Suite(signatureProvider.signer(signingKey));
default -> throw new SigningServiceException(String.format("Proof Type %s is not supported.", ldpType)); default -> throw new SigningServiceException(String.format("Proof Type %s is not supported.", ldpType));
}; };
} }

View file

@ -17,7 +17,6 @@
package org.keycloak.protocol.oid4vc.issuance.signing; package org.keycloak.protocol.oid4vc.issuance.signing;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException; import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -39,15 +38,13 @@ public class LDSigningServiceProviderFactory implements VCSigningServiceProvider
public static final String SUPPORTED_FORMAT = Format.LDP_VC; public static final String SUPPORTED_FORMAT = Format.LDP_VC;
private static final String HELP_TEXT = "Issues Verifiable Credentials in the W3C Data Model, using Linked-Data Proofs. See https://www.w3.org/TR/vc-data-model/"; private static final String HELP_TEXT = "Issues Verifiable Credentials in the W3C Data Model, using Linked-Data Proofs. See https://www.w3.org/TR/vc-data-model/";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override @Override
public VerifiableCredentialsSigningService create(KeycloakSession session, ComponentModel model) { public VerifiableCredentialsSigningService create(KeycloakSession session, ComponentModel model) {
String keyId = model.get(SigningProperties.KEY_ID.getKey()); String keyId = model.get(SigningProperties.KEY_ID.getKey());
String proofType = model.get(SigningProperties.PROOF_TYPE.getKey()); String proofType = model.get(SigningProperties.PROOF_TYPE.getKey());
String algorithmType = model.get(SigningProperties.ALGORITHM_TYPE.getKey()); String algorithmType = model.get(SigningProperties.ALGORITHM_TYPE.getKey());
Optional<String> kid = Optional.ofNullable(model.get(SigningProperties.KID_HEADER.getKey())); Optional<String> kid = Optional.ofNullable(model.get(SigningProperties.KID_HEADER.getKey()));
return new LDSigningService(session, keyId, algorithmType, proofType, OBJECT_MAPPER, new OffsetTimeProvider(), kid); return new LDSigningService(session, keyId, algorithmType, proofType, new OffsetTimeProvider(), kid);
} }
@Override @Override
@ -58,10 +55,10 @@ public class LDSigningServiceProviderFactory implements VCSigningServiceProvider
@Override @Override
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return VCSigningServiceProviderFactory.configurationBuilder() return VCSigningServiceProviderFactory.configurationBuilder()
.property(SigningProperties.ALGORITHM_TYPE.asConfigProperty()) .property(SigningProperties.ALGORITHM_TYPE.asConfigProperty())
.property(SigningProperties.PROOF_TYPE.asConfigProperty()) .property(SigningProperties.PROOF_TYPE.asConfigProperty())
.property(SigningProperties.KID_HEADER.asConfigProperty()) .property(SigningProperties.KID_HEADER.asConfigProperty())
.build(); .build();
} }
@Override @Override
@ -72,8 +69,8 @@ public class LDSigningServiceProviderFactory implements VCSigningServiceProvider
@Override @Override
public void validateSpecificConfiguration(KeycloakSession session, RealmModel realm, ComponentModel model) throws ComponentValidationException { public void validateSpecificConfiguration(KeycloakSession session, RealmModel realm, ComponentModel model) throws ComponentValidationException {
ConfigurationValidationHelper.check(model) ConfigurationValidationHelper.check(model)
.checkRequired(SigningProperties.ALGORITHM_TYPE.asConfigProperty()) .checkRequired(SigningProperties.ALGORITHM_TYPE.asConfigProperty())
.checkRequired(SigningProperties.PROOF_TYPE.asConfigProperty()); .checkRequired(SigningProperties.PROOF_TYPE.asConfigProperty());
} }
@Override @Override

View file

@ -32,6 +32,7 @@ import org.keycloak.sdjwt.DisclosureSpec;
import org.keycloak.sdjwt.SdJwt; import org.keycloak.sdjwt.SdJwt;
import org.keycloak.sdjwt.SdJwtUtils; import org.keycloak.sdjwt.SdJwtUtils;
import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -121,7 +122,7 @@ public class SdJwtSigningService extends SigningService<String> {
// Get the issuance date from the credential. Since nbf is mandatory, we set it to the current time if not // Get the issuance date from the credential. Since nbf is mandatory, we set it to the current time if not
// provided // provided
long iat = Optional.ofNullable(verifiableCredential.getIssuanceDate()) long iat = Optional.ofNullable(verifiableCredential.getIssuanceDate())
.map(issuanceDate -> issuanceDate.toInstant().getEpochSecond()) .map(Instant::getEpochSecond)
.orElse((long) timeProvider.currentTimeSeconds()); .orElse((long) timeProvider.currentTimeSeconds());
rootNode.put(NOT_BEFORE_CLAIM, iat); rootNode.put(NOT_BEFORE_CLAIM, iat);
if (verifiableCredential.getType() == null || verifiableCredential.getType().size() != 1) { if (verifiableCredential.getType() == null || verifiableCredential.getType().size() != 1) {

View file

@ -31,13 +31,13 @@ import com.apicatalog.rdf.io.RdfWriter;
import com.apicatalog.rdf.io.error.RdfWriterException; import com.apicatalog.rdf.io.error.RdfWriterException;
import com.apicatalog.rdf.io.error.UnsupportedContentException; import com.apicatalog.rdf.io.error.UnsupportedContentException;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.setl.rdf.normalization.RdfNormalize; import io.setl.rdf.normalization.RdfNormalize;
import jakarta.json.JsonObject; import jakarta.json.JsonObject;
import jakarta.json.JsonValue; import jakarta.json.JsonValue;
import org.keycloak.crypto.SignatureSignerContext; import org.keycloak.crypto.SignatureSignerContext;
import org.keycloak.protocol.oid4vc.issuance.signing.SigningServiceException; import org.keycloak.protocol.oid4vc.issuance.signing.SigningServiceException;
import org.keycloak.protocol.oid4vc.model.VerifiableCredential; import org.keycloak.protocol.oid4vc.model.VerifiableCredential;
import org.keycloak.util.JsonSerialization;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
@ -59,13 +59,11 @@ import java.util.Optional;
*/ */
public class Ed255192018Suite implements LinkedDataCryptographicSuite { public class Ed255192018Suite implements LinkedDataCryptographicSuite {
private final ObjectMapper objectMapper;
private final SignatureSignerContext signerContext; private final SignatureSignerContext signerContext;
public static final String PROOF_TYPE = "Ed25519Signature2018"; public static final String PROOF_TYPE = "Ed25519Signature2018";
public Ed255192018Suite(ObjectMapper objectMapper, SignatureSignerContext signerContext) { public Ed255192018Suite(SignatureSignerContext signerContext) {
this.objectMapper = objectMapper;
this.signerContext = signerContext; this.signerContext = signerContext;
} }
@ -79,7 +77,7 @@ public class Ed255192018Suite implements LinkedDataCryptographicSuite {
private byte[] transform(VerifiableCredential verifiableCredential) { private byte[] transform(VerifiableCredential verifiableCredential) {
try { try {
String credentialString = objectMapper.writeValueAsString(verifiableCredential); String credentialString = JsonSerialization.mapper.writeValueAsString(verifiableCredential);
var credentialDocument = JsonDocument.of(new StringReader(credentialString)); var credentialDocument = JsonDocument.of(new StringReader(credentialString));

View file

@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.net.URI; import java.net.URI;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -50,9 +51,9 @@ public class VerifiableCredential {
private List<String> context = new ArrayList<>(List.of(VC_CONTEXT_V1)); private List<String> context = new ArrayList<>(List.of(VC_CONTEXT_V1));
private List<String> type = new ArrayList<>(); private List<String> type = new ArrayList<>();
private URI issuer; private URI issuer;
private Date issuanceDate; private Instant issuanceDate;
private URI id; private URI id;
private Date expirationDate; private Instant expirationDate;
private CredentialSubject credentialSubject = new CredentialSubject(); private CredentialSubject credentialSubject = new CredentialSubject();
@JsonIgnore @JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<>(); private Map<String, Object> additionalProperties = new HashMap<>();
@ -95,11 +96,11 @@ public class VerifiableCredential {
return this; return this;
} }
public Date getIssuanceDate() { public Instant getIssuanceDate() {
return issuanceDate; return issuanceDate;
} }
public VerifiableCredential setIssuanceDate(Date issuanceDate) { public VerifiableCredential setIssuanceDate(Instant issuanceDate) {
this.issuanceDate = issuanceDate; this.issuanceDate = issuanceDate;
return this; return this;
} }
@ -113,11 +114,11 @@ public class VerifiableCredential {
return this; return this;
} }
public Date getExpirationDate() { public Instant getExpirationDate() {
return expirationDate; return expirationDate;
} }
public VerifiableCredential setExpirationDate(Date expirationDate) { public VerifiableCredential setExpirationDate(Instant expirationDate) {
this.expirationDate = expirationDate; this.expirationDate = expirationDate;
return this; return this;
} }

View file

@ -17,7 +17,6 @@
package org.keycloak.testsuite.oid4vc.issuance.signing; package org.keycloak.testsuite.oid4vc.issuance.signing;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -38,10 +37,10 @@ import org.keycloak.protocol.oid4vc.model.VerifiableCredential;
import org.keycloak.representations.JsonWebToken; import org.keycloak.representations.JsonWebToken;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.runonserver.RunOnServerException; import org.keycloak.testsuite.runonserver.RunOnServerException;
import org.keycloak.util.JsonSerialization;
import java.security.PublicKey; import java.security.PublicKey;
import java.time.Instant; import java.time.Instant;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -126,7 +125,7 @@ public class JwtSigningServiceTest extends OID4VCTest {
Map.of("id", String.format("uri:uuid:%s", UUID.randomUUID()), Map.of("id", String.format("uri:uuid:%s", UUID.randomUUID()),
"test", "test", "test", "test",
"arrayClaim", List.of("a", "b", "c"), "arrayClaim", List.of("a", "b", "c"),
"issuanceDate", Date.from(Instant.ofEpochSecond(10))))); "issuanceDate", Instant.ofEpochSecond(10))));
} }
@Test @Test
@ -184,19 +183,19 @@ public class JwtSigningServiceTest extends OID4VCTest {
try { try {
JsonWebToken theToken = verifier.getToken(); JsonWebToken theToken = verifier.getToken();
assertEquals("JWT claim in JWT encoded VC or VP MUST be used to set the value of the “expirationDate” of the VC", TEST_EXPIRATION_DATE.toInstant().getEpochSecond(), theToken.getExp().longValue()); assertEquals("JWT claim in JWT encoded VC or VP MUST be used to set the value of the “expirationDate” of the VC", TEST_EXPIRATION_DATE.getEpochSecond(), theToken.getExp().longValue());
if (claims.containsKey("issuanceDate")) { if (claims.containsKey("issuanceDate")) {
assertEquals("VC Data Model v1.1 specifies that “issuanceDate” property MUST be represented as an nbf JWT claim, and not iat JWT claim.", ((Date) claims.get("issuanceDate")).toInstant().getEpochSecond(), theToken.getNbf().longValue()); assertEquals("VC Data Model v1.1 specifies that “issuanceDate” property MUST be represented as an nbf JWT claim, and not iat JWT claim.", ((Instant) claims.get("issuanceDate")).getEpochSecond(), theToken.getNbf().longValue());
} else { } else {
// if not specific date is set, check against "currentTime" // if not specific date is set, check against "currentTime"
assertEquals("VC Data Model v1.1 specifies that “issuanceDate” property MUST be represented as an nbf JWT claim, and not iat JWT claim.", TEST_ISSUANCE_DATE.toInstant().getEpochSecond(), theToken.getNbf().longValue()); assertEquals("VC Data Model v1.1 specifies that “issuanceDate” property MUST be represented as an nbf JWT claim, and not iat JWT claim.", TEST_ISSUANCE_DATE.getEpochSecond(), theToken.getNbf().longValue());
} }
assertEquals("The issuer should be set in the token.", TEST_DID.toString(), theToken.getIssuer()); assertEquals("The issuer should be set in the token.", TEST_DID.toString(), theToken.getIssuer());
assertEquals("The credential ID should be set as the token ID.", testCredential.getId().toString(), theToken.getId()); assertEquals("The credential ID should be set as the token ID.", testCredential.getId().toString(), theToken.getId());
Optional.ofNullable(testCredential.getCredentialSubject().getClaims().get("id")).ifPresent(id -> assertEquals("If the credentials subject id is set, it should be set as the token subject.", id.toString(), theToken.getSubject())); Optional.ofNullable(testCredential.getCredentialSubject().getClaims().get("id")).ifPresent(id -> assertEquals("If the credentials subject id is set, it should be set as the token subject.", id.toString(), theToken.getSubject()));
assertNotNull("The credentials should be included at the vc-claim.", theToken.getOtherClaims().get("vc")); assertNotNull("The credentials should be included at the vc-claim.", theToken.getOtherClaims().get("vc"));
VerifiableCredential credential = new ObjectMapper().convertValue(theToken.getOtherClaims().get("vc"), VerifiableCredential.class); VerifiableCredential credential = JsonSerialization.mapper.convertValue(theToken.getOtherClaims().get("vc"), VerifiableCredential.class);
assertEquals("The types should be included", TEST_TYPES, credential.getType()); assertEquals("The types should be included", TEST_TYPES, credential.getType());
assertEquals("The issuer should be included", TEST_DID, credential.getIssuer()); assertEquals("The issuer should be included", TEST_DID, credential.getIssuer());
assertEquals("The expiration date should be included", TEST_EXPIRATION_DATE, credential.getExpirationDate()); assertEquals("The expiration date should be included", TEST_EXPIRATION_DATE, credential.getExpirationDate());

View file

@ -17,7 +17,6 @@
package org.keycloak.testsuite.oid4vc.issuance.signing; package org.keycloak.testsuite.oid4vc.issuance.signing;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.common.crypto.CryptoIntegration;
@ -33,7 +32,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.runonserver.RunOnServerException; import org.keycloak.testsuite.runonserver.RunOnServerException;
import java.time.Instant; import java.time.Instant;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -61,7 +59,6 @@ public class LDSigningServiceTest extends OID4VCTest {
getKeyFromSession(session).getKid(), getKeyFromSession(session).getKid(),
"EdDSA", "EdDSA",
"UnsupportedSignatureType", "UnsupportedSignatureType",
new ObjectMapper(),
new StaticTimeProvider(1000), new StaticTimeProvider(1000),
Optional.empty())); Optional.empty()));
} catch (RunOnServerException ros) { } catch (RunOnServerException ros) {
@ -81,7 +78,6 @@ public class LDSigningServiceTest extends OID4VCTest {
"no-such-key", "no-such-key",
"EdDSA", "EdDSA",
"Ed25519Signature2018", "Ed25519Signature2018",
new ObjectMapper(),
new StaticTimeProvider(1000), new StaticTimeProvider(1000),
Optional.empty())); Optional.empty()));
} catch (RunOnServerException ros) { } catch (RunOnServerException ros) {
@ -113,7 +109,7 @@ public class LDSigningServiceTest extends OID4VCTest {
Map.of("id", String.format("uri:uuid:%s", UUID.randomUUID()), Map.of("id", String.format("uri:uuid:%s", UUID.randomUUID()),
"test", "test", "test", "test",
"arrayClaim", List.of("a", "b", "c"), "arrayClaim", List.of("a", "b", "c"),
"issuanceDate", Date.from(Instant.ofEpochSecond(10))), "issuanceDate", Instant.ofEpochSecond(10)),
Optional.empty())); Optional.empty()));
} }
@ -127,7 +123,7 @@ public class LDSigningServiceTest extends OID4VCTest {
Map.of("id", String.format("uri:uuid:%s", UUID.randomUUID()), Map.of("id", String.format("uri:uuid:%s", UUID.randomUUID()),
"test", "test", "test", "test",
"arrayClaim", List.of("a", "b", "c"), "arrayClaim", List.of("a", "b", "c"),
"issuanceDate", Date.from(Instant.ofEpochSecond(10))), "issuanceDate", Instant.ofEpochSecond(10)),
Optional.of("did:web:test.org#the-key-id"))); Optional.of("did:web:test.org#the-key-id")));
} }
@ -150,7 +146,6 @@ public class LDSigningServiceTest extends OID4VCTest {
keyWrapper.getKid(), keyWrapper.getKid(),
"EdDSA", "EdDSA",
"Ed25519Signature2018", "Ed25519Signature2018",
new ObjectMapper(),
new StaticTimeProvider(1000), new StaticTimeProvider(1000),
kid); kid);

View file

@ -51,7 +51,6 @@ import org.keycloak.common.util.SecretGenerator;
import org.keycloak.crypto.Algorithm; import org.keycloak.crypto.Algorithm;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oid4vc.OID4VCLoginProtocolFactory;
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint; import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProviderFactory; import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProviderFactory;
import org.keycloak.protocol.oid4vc.issuance.TimeProvider; import org.keycloak.protocol.oid4vc.issuance.TimeProvider;
@ -89,10 +88,8 @@ import java.nio.charset.StandardCharsets;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -355,7 +352,6 @@ public class OID4VCIssuerEndpointTest extends OID4VCTest {
@Test @Test
public void testRequestCredential() { public void testRequestCredential() {
String token = getBearerToken(oauth); String token = getBearerToken(oauth);
ObjectMapper objectMapper = new ObjectMapper();
testingClient testingClient
.server(TEST_REALM_NAME) .server(TEST_REALM_NAME)
.run((session -> { .run((session -> {
@ -373,7 +369,9 @@ public class OID4VCIssuerEndpointTest extends OID4VCTest {
assertNotNull("A valid credential string should have been responded", jsonWebToken); assertNotNull("A valid credential string should have been responded", jsonWebToken);
assertNotNull("The credentials should be included at the vc-claim.", jsonWebToken.getOtherClaims().get("vc")); assertNotNull("The credentials should be included at the vc-claim.", jsonWebToken.getOtherClaims().get("vc"));
VerifiableCredential credential = objectMapper.convertValue(jsonWebToken.getOtherClaims().get("vc"), VerifiableCredential.class); VerifiableCredential credential = //
JsonSerialization.mapper.convertValue(jsonWebToken.getOtherClaims().get(
"vc"), VerifiableCredential.class);
assertNotNull("@context is a required VC property", credential.getContext()); assertNotNull("@context is a required VC property", credential.getContext());
assertEquals(1, credential.getContext().size()); assertEquals(1, credential.getContext().size());
assertEquals(VerifiableCredential.VC_CONTEXT_V1, credential.getContext().get(0)); assertEquals(VerifiableCredential.VC_CONTEXT_V1, credential.getContext().get(0));
@ -613,7 +611,7 @@ public class OID4VCIssuerEndpointTest extends OID4VCTest {
JsonWebToken jsonWebToken = TokenVerifier.create((String) credentialResponse.getCredential(), JsonWebToken.class).getToken(); JsonWebToken jsonWebToken = TokenVerifier.create((String) credentialResponse.getCredential(), JsonWebToken.class).getToken();
assertEquals("did:web:test.org", jsonWebToken.getIssuer()); assertEquals("did:web:test.org", jsonWebToken.getIssuer());
VerifiableCredential credential = new ObjectMapper().convertValue(jsonWebToken.getOtherClaims().get("vc"), VerifiableCredential.class); VerifiableCredential credential = JsonSerialization.mapper.convertValue(jsonWebToken.getOtherClaims().get("vc"), VerifiableCredential.class);
assertEquals(TEST_TYPES, credential.getType()); assertEquals(TEST_TYPES, credential.getType());
assertEquals(TEST_DID, credential.getIssuer()); assertEquals(TEST_DID, credential.getIssuer());
assertEquals("john@email.cz", credential.getCredentialSubject().getClaims().get("email")); assertEquals("john@email.cz", credential.getCredentialSubject().getClaims().get("email"));
@ -704,7 +702,7 @@ public class OID4VCIssuerEndpointTest extends OID4VCTest {
assertNotNull("The credential should have been responded.", credentialResponse.getCredential()); assertNotNull("The credential should have been responded.", credentialResponse.getCredential());
JsonWebToken jsonWebToken = TokenVerifier.create((String) credentialResponse.getCredential(), JsonWebToken.class).getToken(); JsonWebToken jsonWebToken = TokenVerifier.create((String) credentialResponse.getCredential(), JsonWebToken.class).getToken();
assertEquals("did:web:test.org", jsonWebToken.getIssuer()); assertEquals("did:web:test.org", jsonWebToken.getIssuer());
VerifiableCredential credential = new ObjectMapper().convertValue(jsonWebToken.getOtherClaims().get("vc"), VerifiableCredential.class); VerifiableCredential credential = JsonSerialization.mapper.convertValue(jsonWebToken.getOtherClaims().get("vc"), VerifiableCredential.class);
assertEquals(List.of("VerifiableCredential"), credential.getType()); assertEquals(List.of("VerifiableCredential"), credential.getType());
assertEquals(URI.create("did:web:test.org"), credential.getIssuer()); assertEquals(URI.create("did:web:test.org"), credential.getIssuer());
assertEquals("john@email.cz", credential.getCredentialSubject().getClaims().get("email")); assertEquals("john@email.cz", credential.getCredentialSubject().getClaims().get("email"));

View file

@ -52,7 +52,6 @@ import java.security.PublicKey;
import java.security.Security; import java.security.Security;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.time.Instant; import java.time.Instant;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -67,8 +66,8 @@ public abstract class OID4VCTest extends AbstractTestRealmKeycloakTest {
protected static final String CONTEXT_URL = "https://www.w3.org/2018/credentials/v1"; protected static final String CONTEXT_URL = "https://www.w3.org/2018/credentials/v1";
protected static final URI TEST_DID = URI.create("did:web:test.org"); protected static final URI TEST_DID = URI.create("did:web:test.org");
protected static final List<String> TEST_TYPES = List.of("VerifiableCredential"); protected static final List<String> TEST_TYPES = List.of("VerifiableCredential");
protected static final Date TEST_EXPIRATION_DATE = Date.from(Instant.ofEpochSecond(2000)); protected static final Instant TEST_EXPIRATION_DATE = Instant.ofEpochSecond(2000);
protected static final Date TEST_ISSUANCE_DATE = Date.from(Instant.ofEpochSecond(1000)); protected static final Instant TEST_ISSUANCE_DATE = Instant.ofEpochSecond(1000);
protected static final KeyWrapper RSA_KEY = getRsaKey(); protected static final KeyWrapper RSA_KEY = getRsaKey();
@ -87,7 +86,7 @@ public abstract class OID4VCTest extends AbstractTestRealmKeycloakTest {
testCredential.setIssuer(TEST_DID); testCredential.setIssuer(TEST_DID);
testCredential.setExpirationDate(TEST_EXPIRATION_DATE); testCredential.setExpirationDate(TEST_EXPIRATION_DATE);
if (claims.containsKey("issuanceDate")) { if (claims.containsKey("issuanceDate")) {
testCredential.setIssuanceDate((Date) claims.get("issuanceDate")); testCredential.setIssuanceDate((Instant) claims.get("issuanceDate"));
} }
testCredential.setCredentialSubject(getCredentialSubject(claims)); testCredential.setCredentialSubject(getCredentialSubject(claims));

View file

@ -243,7 +243,7 @@ public class SdJwtSigningServiceTest extends OID4VCTest {
assertEquals("The credential ID should be set as the token ID.", testCredential.getId().toString(), theToken.getId()); assertEquals("The credential ID should be set as the token ID.", testCredential.getId().toString(), theToken.getId());
assertEquals("The type should be included", TEST_TYPES.get(0), theToken.getOtherClaims().get("vct")); assertEquals("The type should be included", TEST_TYPES.get(0), theToken.getOtherClaims().get("vct"));
assertEquals("The nbf date should be included", TEST_ISSUANCE_DATE.toInstant().getEpochSecond(), theToken.getNbf().longValue()); assertEquals("The nbf date should be included", TEST_ISSUANCE_DATE.getEpochSecond(), theToken.getNbf().longValue());
List<String> sds = (List<String>) theToken.getOtherClaims().get("_sd"); List<String> sds = (List<String>) theToken.getOtherClaims().get("_sd");
if (sds != null && !sds.isEmpty()){ if (sds != null && !sds.isEmpty()){