Skip to content

Commit 7823365

Browse files
fix: use numeric JWT claims instead of ISO-8601
1 parent 1c3a711 commit 7823365

File tree

18 files changed

+67
-72
lines changed

18 files changed

+67
-72
lines changed

core/common/token-core/src/test/java/org/eclipse/edc/jwt/rules/ExpirationIssuedAtValidationRuleTest.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.eclipse.edc.token.spi.TokenValidationRule;
2020
import org.junit.jupiter.api.Test;
2121

22-
import java.sql.Date;
2322
import java.time.Clock;
2423
import java.time.Instant;
2524
import java.time.temporal.ChronoUnit;
@@ -39,7 +38,7 @@ class ExpirationIssuedAtValidationRuleTest {
3938
@Test
4039
void validationOk() {
4140
var token = ClaimToken.Builder.newInstance()
42-
.claim(EXPIRATION_TIME, Date.from(now.plusSeconds(600)))
41+
.claim(EXPIRATION_TIME, now.plusSeconds(600).getEpochSecond())
4342
.build();
4443

4544
var result = rule.checkRule(token, emptyMap());
@@ -50,7 +49,7 @@ void validationOk() {
5049
@Test
5150
void validationKoBecauseExpirationTimeNotRespected() {
5251
var token = ClaimToken.Builder.newInstance()
53-
.claim(EXPIRATION_TIME, Date.from(now.minusSeconds(10)))
52+
.claim(EXPIRATION_TIME, now.minusSeconds(10).getEpochSecond())
5453
.build();
5554

5655
var result = rule.checkRule(token, emptyMap());
@@ -85,8 +84,8 @@ void validationKoBecauseExpirationTimeNotProvided_doesNotAllowNull() {
8584
@Test
8685
void validationKoBecauseIssuedAtAfterExpires() {
8786
var token = ClaimToken.Builder.newInstance()
88-
.claim(EXPIRATION_TIME, Date.from(now.plusSeconds(60)))
89-
.claim(ISSUED_AT, Date.from(now.plusSeconds(65)))
87+
.claim(EXPIRATION_TIME, now.plusSeconds(60).getEpochSecond())
88+
.claim(ISSUED_AT, now.plusSeconds(65).getEpochSecond())
9089
.build();
9190

9291
var result = rule.checkRule(token, emptyMap());
@@ -98,8 +97,8 @@ void validationKoBecauseIssuedAtAfterExpires() {
9897
@Test
9998
void validationKoBecauseIssuedAtInFuture() {
10099
var token = ClaimToken.Builder.newInstance()
101-
.claim(EXPIRATION_TIME, Date.from(now.plusSeconds(60)))
102-
.claim(ISSUED_AT, Date.from(now.plusSeconds(10)))
100+
.claim(EXPIRATION_TIME, now.plusSeconds(60).getEpochSecond())
101+
.claim(ISSUED_AT, now.plusSeconds(10).getEpochSecond())
103102
.build();
104103

105104
var result = rule.checkRule(token, emptyMap());
@@ -113,8 +112,8 @@ void validationKoBecauseIssuedAtInFutureOutsideLeeway() {
113112
var rule = new ExpirationIssuedAtValidationRule(clock, 5, false);
114113

115114
var token = ClaimToken.Builder.newInstance()
116-
.claim(EXPIRATION_TIME, Date.from(now.plusSeconds(60)))
117-
.claim(ISSUED_AT, Date.from(now.plusSeconds(10)))
115+
.claim(EXPIRATION_TIME, now.plusSeconds(60).getEpochSecond())
116+
.claim(ISSUED_AT, now.plusSeconds(10).getEpochSecond())
118117
.build();
119118

120119
var result = rule.checkRule(token, emptyMap());
@@ -128,8 +127,8 @@ void validationOkBecauseIssuedAtInFutureButWithinLeeway() {
128127
var rule = new ExpirationIssuedAtValidationRule(clock, 20, false);
129128

130129
var token = ClaimToken.Builder.newInstance()
131-
.claim(EXPIRATION_TIME, Date.from(now.plusSeconds(60)))
132-
.claim(ISSUED_AT, Date.from(now.plusSeconds(10)))
130+
.claim(EXPIRATION_TIME, now.plusSeconds(60).getEpochSecond())
131+
.claim(ISSUED_AT, now.plusSeconds(10).getEpochSecond())
133132
.build();
134133

135134
var result = rule.checkRule(token, emptyMap());
@@ -155,8 +154,8 @@ void validationKoWithRoundedIssuedAtAndNoLeeway() {
155154
var rule = new ExpirationIssuedAtValidationRule(clock, 0, false);
156155

157156
var token = ClaimToken.Builder.newInstance()
158-
.claim(EXPIRATION_TIME, Date.from(expiresAt))
159-
.claim(ISSUED_AT, Date.from(issuedAt))
157+
.claim(EXPIRATION_TIME, expiresAt.getEpochSecond())
158+
.claim(ISSUED_AT, expiresAt.getEpochSecond())
160159
.build();
161160

162161
var result = rule.checkRule(token, emptyMap());
@@ -183,8 +182,8 @@ void validationOkWithRoundedIssuedAtAndMinimalLeeway() {
183182
var rule = new ExpirationIssuedAtValidationRule(clock, 2, false);
184183

185184
var token = ClaimToken.Builder.newInstance()
186-
.claim(EXPIRATION_TIME, Date.from(expiresAt))
187-
.claim(ISSUED_AT, Date.from(issuedAt))
185+
.claim(EXPIRATION_TIME, expiresAt.getEpochSecond())
186+
.claim(ISSUED_AT, issuedAt.getEpochSecond())
188187
.build();
189188

190189
var result = rule.checkRule(token, emptyMap());

core/common/token-core/src/test/java/org/eclipse/edc/jwt/rules/NotBeforeValidationRuleTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.eclipse.edc.token.spi.TokenValidationRule;
2020
import org.junit.jupiter.api.Test;
2121

22-
import java.sql.Date;
2322
import java.time.Clock;
2423
import java.time.Instant;
2524
import java.time.temporal.ChronoUnit;
@@ -39,7 +38,7 @@ class NotBeforeValidationRuleTest {
3938
@Test
4039
void validNotBefore() {
4140
var token = ClaimToken.Builder.newInstance()
42-
.claim(NOT_BEFORE, Date.from(now.plusSeconds(notBeforeLeeway)))
41+
.claim(NOT_BEFORE, now.plusSeconds(notBeforeLeeway).getEpochSecond())
4342
.build();
4443

4544
var result = rule.checkRule(token, emptyMap());
@@ -50,7 +49,7 @@ void validNotBefore() {
5049
@Test
5150
void validationKoBecauseNotBeforeTimeNotRespected() {
5251
var token = ClaimToken.Builder.newInstance()
53-
.claim(NOT_BEFORE, Date.from(now.plusSeconds(notBeforeLeeway + 1)))
52+
.claim(NOT_BEFORE, now.plusSeconds(notBeforeLeeway + 1).getEpochSecond())
5453
.build();
5554

5655
var result = rule.checkRule(token, emptyMap());

extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/DcpDefaultServicesExtension.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636

3737
import java.time.Clock;
3838
import java.util.Map;
39-
import java.util.function.Consumer;
4039

4140
import static org.eclipse.edc.spi.result.Result.success;
4241

@@ -99,10 +98,4 @@ public ClaimTokenCreatorFunction defaultClaimTokenFunction() {
9998
};
10099
}
101100

102-
private void checkProperty(String key, String value, Consumer<String> onMissing) {
103-
if (value == null) {
104-
onMissing.accept("No setting found for key '%s'.".formatted(key));
105-
}
106-
}
107-
108101
}

extensions/common/iam/identity-trust/identity-trust-service/src/main/java/org/eclipse/edc/iam/identitytrust/service/IdentityAndTrustService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public Result<TokenRepresentation> obtainClientCredentials(TokenParameters param
110110
}
111111

112112
// create claims for the STS
113-
var claims = new HashMap<String, String>();
113+
var claims = new HashMap<String, Object>();
114114
parameters.getClaims().forEach((k, v) -> claims.replace(k, v.toString()));
115115

116116
claims.putAll(Map.of(
@@ -134,12 +134,12 @@ public Result<ClaimToken> verifyJwtToken(TokenRepresentation tokenRepresentation
134134
var accessToken = claimToken.getStringClaim(PRESENTATION_TOKEN_CLAIM);
135135
var issuer = claimToken.getStringClaim(ISSUER);
136136

137-
var siTokenClaims = Map.of(PRESENTATION_TOKEN_CLAIM, accessToken,
138-
ISSUED_AT, Instant.now().toString(),
137+
Map<String, Object> siTokenClaims = Map.of(PRESENTATION_TOKEN_CLAIM, accessToken,
138+
ISSUED_AT, Instant.now().getEpochSecond(),
139139
AUDIENCE, issuer,
140140
ISSUER, myOwnDid,
141141
SUBJECT, myOwnDid,
142-
EXPIRATION_TIME, Instant.now().plus(5, ChronoUnit.MINUTES).toString());
142+
EXPIRATION_TIME, Instant.now().plus(5, ChronoUnit.MINUTES).getEpochSecond());
143143
var siToken = secureTokenService.createToken(siTokenClaims, null);
144144
if (siToken.failed()) {
145145
return siToken.mapFailure();

extensions/common/iam/identity-trust/identity-trust-sts/lib/identity-trust-sts-remote-lib/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ public RemoteSecureTokenService(Oauth2Client oauth2Client, StsRemoteClientConfig
5151
}
5252

5353
@Override
54-
public Result<TokenRepresentation> createToken(Map<String, String> claims, @Nullable String bearerAccessScope) {
54+
public Result<TokenRepresentation> createToken(Map<String, Object> claims, @Nullable String bearerAccessScope) {
5555
return createRequest(claims, bearerAccessScope)
5656
.compose(oauth2Client::requestToken);
5757
}
5858

5959
@NotNull
60-
private Result<Oauth2CredentialsRequest> createRequest(Map<String, String> claims, @Nullable String bearerAccessScope) {
60+
private Result<Oauth2CredentialsRequest> createRequest(Map<String, Object> claims, @Nullable String bearerAccessScope) {
6161

6262
var secret = vault.resolveSecret(configuration.clientSecretAlias());
6363
if (secret != null) {

extensions/common/iam/oauth2/oauth2-client/src/main/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private static FormBody createRequestBody(Oauth2CredentialsRequest request) {
6363
var builder = new FormBody.Builder();
6464
request.getParams().entrySet().stream()
6565
.filter(entry -> entry.getValue() != null)
66-
.forEach(entry -> builder.add(entry.getKey(), entry.getValue()));
66+
.forEach(entry -> builder.add(entry.getKey(), entry.getValue().toString()));
6767
return builder.build();
6868
}
6969

extensions/common/iam/oauth2/oauth2-client/src/test/java/org/eclipse/edc/iam/oauth2/client/Oauth2ClientImplTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void verifyRequestTokenSuccess() {
5555

5656
var formParameters = new Parameters(
5757
request.getParams().entrySet().stream()
58-
.map(entry -> Parameter.param(entry.getKey(), entry.getValue()))
58+
.map(entry -> Parameter.param(entry.getKey(), entry.getValue().toString()))
5959
.collect(Collectors.toList())
6060
);
6161

@@ -75,7 +75,7 @@ void verifyRequestTokenSuccess_withExpiresIn() {
7575

7676
var formParameters = new Parameters(
7777
request.getParams().entrySet().stream()
78-
.map(entry -> Parameter.param(entry.getKey(), entry.getValue()))
78+
.map(entry -> Parameter.param(entry.getKey(), entry.getValue().toString()))
7979
.collect(Collectors.toList())
8080
);
8181

@@ -97,7 +97,7 @@ void verifyRequestTokenSuccess_withExpiresIn_whenNotNumber() {
9797

9898
var formParameters = new Parameters(
9999
request.getParams().entrySet().stream()
100-
.map(entry -> Parameter.param(entry.getKey(), entry.getValue()))
100+
.map(entry -> Parameter.param(entry.getKey(), entry.getValue().toString()))
101101
.collect(Collectors.toList())
102102
);
103103

@@ -118,7 +118,7 @@ void verifyRequestTokenSuccess_withAdditionalProperties() {
118118

119119
var formParameters = new Parameters(
120120
request.getParams().entrySet().stream()
121-
.map(entry -> Parameter.param(entry.getKey(), entry.getValue()))
121+
.map(entry -> Parameter.param(entry.getKey(), entry.getValue().toString()))
122122
.collect(Collectors.toList())
123123
);
124124

extensions/data-plane/data-plane-http-oauth2-core/src/test/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactoryTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ void shouldCreatePrivateKeyRequest_whenPrivateKeyNameIsPresent() throws JOSEExce
116116
})
117117
.extracting(PrivateKeyOauth2CredentialsRequest::getClientAssertion)
118118
.satisfies(assertion -> {
119-
var assertionToken = SignedJWT.parse(assertion);
119+
var assertionToken = SignedJWT.parse(assertion.toString());
120120
var now = clock.instant().truncatedTo(ChronoUnit.SECONDS);
121121
assertThat(assertionToken.verify(new RSASSAVerifier(keyPair.toRSAPublicKey()))).isTrue();
122122
assertThat(assertionToken.getJWTClaimsSet().getClaims())

extensions/data-plane/data-plane-iam/src/main/java/org/eclipse/edc/connector/dataplane/iam/service/DataPlaneAuthorizationServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private TokenParameters createTokenParams(DataFlowStartMessage message) {
140140
.claims(JwtRegisteredClaimNames.AUDIENCE, message.getParticipantId())
141141
.claims(JwtRegisteredClaimNames.ISSUER, ownParticipantId)
142142
.claims(JwtRegisteredClaimNames.SUBJECT, ownParticipantId)
143-
.claims(JwtRegisteredClaimNames.ISSUED_AT, clock.instant().toEpochMilli()) // todo: milli or second?
143+
.claims(JwtRegisteredClaimNames.ISSUED_AT, clock.instant().getEpochSecond())
144144
.build();
145145
}
146146

gradle/libs.versions.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ kafkaClients = "4.0.0"
3939
testcontainers = "1.21.0"
4040
tink = "1.17.0"
4141
dsp-tck = "0.1.1-SNAPSHOT"
42+
dcp-tck = "0.1.1-SNAPSHOT"
4243
junit = "1.12.2"
4344

4445
[libraries]
@@ -106,7 +107,7 @@ postgres = { module = "org.postgresql:postgresql", version.ref = "postgres" }
106107
kafkaClients = { module = "org.apache.kafka:kafka-clients", version.ref = "kafkaClients" }
107108
jsonschema = { module = "com.networknt:json-schema-validator", version = "1.5.6" }
108109

109-
# DCP-TCK libraries
110+
# DSP-TCK libraries
110111
dsp-tck-runtime = { module = "org.eclipse.dataspacetck.dsp:tck-runtime", version.ref = "dsp-tck" }
111112
dsp-tck-core = { module = "org.eclipse.dataspacetck.dsp:core", version.ref = "dsp-tck" }
112113
dsp-tck-api = { module = "org.eclipse.dataspacetck.dsp:dsp-api", version.ref = "dsp-tck" }
@@ -115,6 +116,12 @@ dsp-tck-contractnegotiation = { module = "org.eclipse.dataspacetck.dsp:dsp-contr
115116
dsp-tck-transferprocess = { module = "org.eclipse.dataspacetck.dsp:dsp-transfer-process", version.ref = "dsp-tck" }
116117
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit" }
117118

119+
# DCP-TCK libraries
120+
dcp-testcases = { module = "org.eclipse.dataspacetck.dcp:dcp-testcases", version.ref = "dcp-tck" }
121+
dcp-tck-runtime = { module = "org.eclipse.dataspacetck.dsp:tck-runtime", version.ref = "dcp-tck" }
122+
dcp-system = { module = "org.eclipse.dataspacetck.dcp:dcp-system", version.ref = "dcp-tck" }
123+
dsp-core = { module = "org.eclipse.dataspacetck.dsp:core", version.ref = "dcp-tck" }
124+
118125
[bundles]
119126
jackson = ["jackson-annotations", "jackson-databind"]
120127
jersey-core = ["jersey-server", "jersey-common", "jersey-jackson", "jersey-multipart", "jersey-inject", "jersey-servlet", "jersey-servletcore"]

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ include(":system-tests:bom-tests")
340340
include(":system-tests:dsp-compatibility-tests:connector-under-test")
341341
include(":system-tests:dsp-compatibility-tests:compatibility-test-runner")
342342
include(":system-tests:protocol-tck:tck-extension")
343+
include(":system-tests:dcp-tck-tests:presentation")
343344

344345
// BOM modules ----------------------------------------------------------------
345346
include(":dist:bom:controlplane-base-bom")

spi/common/core-spi/src/main/java/org/eclipse/edc/spi/iam/ClaimToken.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,29 @@ public List<?> getListClaim(String claimName) {
7171
}
7272

7373
/**
74-
* Get the date claim value by name converted to {@link Instant}
74+
* Get the NumericDate claim value by name converted to {@link Instant}. The claim must be a long value.
7575
*
7676
* @param claimName the name of the claim
7777
* @return the claim value, null if it does not exist
7878
*/
7979
public Instant getInstantClaim(String claimName) {
8080
return Optional.of(claims)
8181
.map(it -> it.get(claimName))
82-
.map(Date.class::cast)
82+
.map(o -> {
83+
if (o instanceof Long epoch) {
84+
return epoch;
85+
}
86+
if (o instanceof Date d) {
87+
return d.toInstant().getEpochSecond();
88+
}
89+
return null;
90+
})
8391
.map(this::convertToUtcTime)
8492
.orElse(null);
8593
}
8694

87-
private Instant convertToUtcTime(Date date) {
88-
return date.toInstant().atOffset(UTC).toInstant();
95+
private Instant convertToUtcTime(Long epochSecond) {
96+
return Instant.ofEpochSecond(epochSecond).atOffset(UTC).toInstant();
8997
}
9098

9199
public static class Builder {

spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/iam/identitytrust/spi/SecureTokenService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public interface SecureTokenService {
3434
* if bearerAccessScope != null -> creates a {@code token} claim, which is another JWT containing the scope as claims.
3535
* if bearerAccessScope == null -> creates a normal JWT using all the claims in the map
3636
*/
37-
Result<TokenRepresentation> createToken(Map<String, String> claims, @Nullable String bearerAccessScope);
37+
Result<TokenRepresentation> createToken(Map<String, Object> claims, @Nullable String bearerAccessScope);
3838

3939
}

spi/common/oauth2-spi/src/main/java/org/eclipse/edc/iam/oauth2/spi/Oauth2AssertionDecorator.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.eclipse.edc.token.spi.TokenDecorator;
2121

2222
import java.time.Clock;
23-
import java.util.Date;
2423
import java.util.List;
2524
import java.util.Objects;
2625
import java.util.UUID;
@@ -51,8 +50,8 @@ public TokenParameters.Builder decorate(TokenParameters.Builder tokenParameters)
5150
.claims(ISSUER, clientId)
5251
.claims(SUBJECT, clientId)
5352
.claims(JWT_ID, UUID.randomUUID().toString())
54-
.claims(ISSUED_AT, Date.from(clock.instant()))
55-
.claims(EXPIRATION_TIME, Date.from(clock.instant().plusSeconds(validity)));
53+
.claims(ISSUED_AT, clock.instant().getEpochSecond())
54+
.claims(EXPIRATION_TIME, clock.instant().plusSeconds(validity).getEpochSecond());
5655
}
5756

5857
public static class Builder {

0 commit comments

Comments
 (0)