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:
parent
526286e851
commit
018a0802bc
14 changed files with 54 additions and 60 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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<>();
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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()){
|
||||||
|
|
Loading…
Reference in a new issue