Skip to content

Commit 1ea0b1c

Browse files
authored
chore: change class name to JwtUtils (#1724)
1 parent 3ef48d5 commit 1ea0b1c

File tree

6 files changed

+34
-35
lines changed

6 files changed

+34
-35
lines changed

docs/developer/decision-records/2022-06-19-json-web-token/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
| Claim | Value |
88
| --------------- | ------------------------------------------------------------ |
99
| Issuer | The request source connector [did:web](https://w3c-ccg.github.io/did-method-web/) identifier (example: `did:web:edc.example.com`). This allows the server to verify the JWT signature against the source's public key. |
10-
| Subject | The fixed string `verifiable-credential`. |
10+
| Subject | The same value as in the `Issuer` claim. |
1111
| Audience | The request destination connector IDS endpoint (example: `http://edc.example.com:8181/api/v1/ids/data`). This allows the server to verify the intended audience. |
1212
| JWT ID | A random UUID. |
1313
| Expiration Time | A time set in the near future. |

extensions/iam/decentralized-identity/identity-did-service/src/main/java/org/eclipse/dataspaceconnector/identity/DecentralizedIdentityService.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.eclipse.dataspaceconnector.identity;
1818

1919
import com.nimbusds.jwt.SignedJWT;
20-
import org.eclipse.dataspaceconnector.iam.did.crypto.credentials.VerifiableCredentialFactory;
2120
import org.eclipse.dataspaceconnector.iam.did.crypto.key.KeyConverter;
2221
import org.eclipse.dataspaceconnector.iam.did.spi.credentials.CredentialsVerifier;
2322
import org.eclipse.dataspaceconnector.iam.did.spi.document.DidConstants;
@@ -56,7 +55,7 @@ public DecentralizedIdentityService(DidResolverRegistry resolverRegistry, Creden
5655

5756
@Override
5857
public Result<TokenRepresentation> obtainClientCredentials(TokenParameters parameters) {
59-
var jwt = VerifiableCredentialFactory.create(privateKey, issuer, parameters.getAudience(), clock);
58+
var jwt = JwtUtils.create(privateKey, issuer, issuer, parameters.getAudience(), clock);
6059
var token = jwt.serialize();
6160
return Result.success(TokenRepresentation.Builder.newInstance().token(token).build());
6261
}
@@ -85,7 +84,7 @@ public Result<ClaimToken> verifyJwtToken(TokenRepresentation tokenRepresentation
8584
var publicKeyWrapper = KeyConverter.toPublicKeyWrapper(publicKeyJwk, publicKey.get().getId());
8685

8786
monitor.debug("Verifying JWT with public key...");
88-
var verified = VerifiableCredentialFactory.verify(jwt, publicKeyWrapper, audience);
87+
var verified = JwtUtils.verify(jwt, publicKeyWrapper, audience);
8988
if (verified.failed()) {
9089
verified.getFailureMessages().forEach(m -> monitor.debug(() -> "Failure in token verification: " + m));
9190
return Result.failure("Token could not be verified!");
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
*
1313
*/
1414

15-
package org.eclipse.dataspaceconnector.iam.did.crypto.credentials;
15+
package org.eclipse.dataspaceconnector.identity;
1616

1717
import com.nimbusds.jose.Algorithm;
1818
import com.nimbusds.jose.JOSEException;
@@ -36,31 +36,30 @@
3636
import java.util.UUID;
3737

3838
/**
39-
* Convenience/helper class to generate, verify and deserialize verifiable credentials, which are, in fact, Signed JSON Web Tokens (JWTs).
39+
* Convenience/helper class to generate and verify Signed JSON Web Tokens (JWTs) for communicating between connector instances.
4040
*/
41-
public class VerifiableCredentialFactory {
42-
41+
class JwtUtils {
4342
// RFC 7519 Registered (standard) claims
4443
private static final String ISSUER_CLAIM = "iss";
44+
private static final String SUBJECT_CLAIM = "sub";
4545
private static final String EXPIRATION_TIME_CLAIM = "exp";
4646

47-
// Subject claim value
48-
public static final String VERIFIABLE_CREDENTIAL = "verifiable-credential";
49-
5047
/**
51-
* Creates a signed JWT {@link SignedJWT} that contains a set of claims and an issuer. Although all private key types are possible, in the context of Distributed Identity and ION
48+
* Creates a signed JWT {@link SignedJWT} that contains a set of claims and an issuer. Although all private key types are possible, in the context of Distributed Identity
5249
* using an Elliptic Curve key ({@code P-256}) is advisable.
5350
*
5451
* @param privateKey A Private Key represented as {@link PrivateKeyWrapper}.
55-
* @param issuer the "owner" of the VC, in most cases this will be the DID ID. The VC will store this in the "iss" claim
56-
* @param audience the audience of the token, e.g. the IDS Webhook address. The VC will store this in the "aud" claim
57-
* @param clock clock used to get current time
58-
* @return a {@code SignedJWT} that is signed with the private key and contains all claims listed
52+
* @param issuer the value of the token issuer claim.
53+
* @param subject the value of the token subject claim. For Distributed Identity, this value is identical to the issuer claim.
54+
* @param audience the value of the token audience claim, e.g. the IDS Webhook address.
55+
* @param clock clock used to get current time.
56+
* @return a {@code SignedJWT} that is signed with the private key and contains all claims listed.
5957
*/
60-
public static SignedJWT create(PrivateKeyWrapper privateKey, String issuer, String audience, Clock clock) {
61-
var claimsSet = new JWTClaimsSet.Builder().issuer(issuer)
58+
59+
static SignedJWT create(PrivateKeyWrapper privateKey, String issuer, String subject, String audience, Clock clock) {
60+
var claimsSet = new JWTClaimsSet.Builder()
6261
.issuer(issuer)
63-
.subject(VERIFIABLE_CREDENTIAL)
62+
.subject(subject)
6463
.audience(audience)
6564
.expirationTime(Date.from(clock.instant().plus(10, ChronoUnit.MINUTES)))
6665
.jwtID(UUID.randomUUID().toString())
@@ -91,7 +90,7 @@ public static SignedJWT create(PrivateKeyWrapper privateKey, String issuer, Stri
9190
* @param audience The intended audience
9291
* @return true if verified, false otherwise
9392
*/
94-
public static Result<Void> verify(SignedJWT jwt, PublicKeyWrapper publicKey, String audience) {
93+
static Result<Void> verify(SignedJWT jwt, PublicKeyWrapper publicKey, String audience) {
9594
// verify JWT signature
9695
try {
9796
var verified = jwt.verify(publicKey.verifier());
@@ -112,10 +111,10 @@ public static Result<Void> verify(SignedJWT jwt, PublicKeyWrapper publicKey, Str
112111
// verify claims
113112
var exactMatchClaims = new JWTClaimsSet.Builder()
114113
.audience(audience)
115-
.subject(VERIFIABLE_CREDENTIAL)
116114
.build();
117115
var requiredClaims = Set.of(
118116
ISSUER_CLAIM,
117+
SUBJECT_CLAIM,
119118
EXPIRATION_TIME_CLAIM);
120119

121120
var claimsVerifier = new DefaultJWTClaimsVerifier<>(exactMatchClaims, requiredClaims);
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*
1414
*/
1515

16-
package org.eclipse.dataspaceconnector.iam.did.crypto.credentials;
16+
package org.eclipse.dataspaceconnector.identity;
1717

1818
import com.nimbusds.jose.JOSEException;
1919
import com.nimbusds.jose.jwk.ECKey;
@@ -46,12 +46,14 @@
4646
import static java.time.temporal.ChronoUnit.SECONDS;
4747
import static org.assertj.core.api.Assertions.assertThat;
4848
import static org.eclipse.dataspaceconnector.iam.did.crypto.key.KeyPairFactory.generateKeyPairP256;
49+
import static org.eclipse.dataspaceconnector.identity.JwtUtils.create;
50+
import static org.eclipse.dataspaceconnector.identity.JwtUtils.verify;
4951
import static org.eclipse.dataspaceconnector.junit.testfixtures.TestUtils.getResourceFileContentAsString;
5052
import static org.mockito.ArgumentMatchers.any;
5153
import static org.mockito.Mockito.mock;
5254
import static org.mockito.Mockito.when;
5355

54-
class VerifiableCredentialFactoryTest {
56+
class JwtUtilsTest {
5557
private static final Faker FAKER = new Faker();
5658

5759
private final Instant now = Instant.now();
@@ -68,19 +70,19 @@ void setup() throws JOSEException {
6870
@Test
6971
@SuppressWarnings("ResultOfMethodCallIgnored")
7072
void createVerifiableCredential() throws Exception {
71-
var vc = VerifiableCredentialFactory.create(privateKey, "test-connector", "test-audience", clock);
73+
var vc = create(privateKey, "test-issuer", "test-subject", "test-audience", clock);
7274

7375
assertThat(vc).isNotNull();
74-
assertThat(vc.getJWTClaimsSet().getIssuer()).isEqualTo("test-connector");
75-
assertThat(vc.getJWTClaimsSet().getSubject()).isEqualTo("verifiable-credential");
76+
assertThat(vc.getJWTClaimsSet().getIssuer()).isEqualTo("test-issuer");
77+
assertThat(vc.getJWTClaimsSet().getSubject()).isEqualTo("test-subject");
7678
assertThat(vc.getJWTClaimsSet().getAudience()).containsExactly("test-audience");
7779
assertThat(vc.getJWTClaimsSet().getJWTID()).satisfies(UUID::fromString);
7880
assertThat(vc.getJWTClaimsSet().getExpirationTime()).isEqualTo(now.plus(10, MINUTES).truncatedTo(SECONDS));
7981
}
8082

8183
@Test
8284
void ensureSerialization() throws Exception {
83-
var vc = VerifiableCredentialFactory.create(privateKey, "test-connector", "test-audience", clock);
85+
var vc = create(privateKey, "test-issuer", "test-subject", "test-audience", clock);
8486

8587
assertThat(vc).isNotNull();
8688
String jwtString = vc.serialize();
@@ -95,17 +97,17 @@ void ensureSerialization() throws Exception {
9597

9698
@Test
9799
void verifyJwt_OnInvalidSignature_fails() {
98-
var jwt = VerifiableCredentialFactory.create(privateKey, "test-connector", "test-audience", clock);
100+
var jwt = create(privateKey, "test-issuer", "test-subject", "test-audience", clock);
99101
var unrelatedPublicKey = new EcPublicKeyWrapper(generateKeyPairP256());
100-
assertThat(VerifiableCredentialFactory.verify(jwt, unrelatedPublicKey, "test-audience").getFailureMessages()).containsExactly("Invalid signature");
102+
assertThat(verify(jwt, unrelatedPublicKey, "test-audience").getFailureMessages()).containsExactly("Invalid signature");
101103
}
102104

103105
@Test
104106
void verifyJwt_OnVerificationFailure_fails() throws Exception {
105107
var jwt = mock(SignedJWT.class);
106108
var message = FAKER.lorem().sentence();
107109
when(jwt.verify(any())).thenThrow(new JOSEException(message));
108-
assertThat(VerifiableCredentialFactory.verify(jwt, publicKey, "test-audience").getFailureMessages())
110+
assertThat(verify(jwt, publicKey, "test-audience").getFailureMessages())
109111
.containsExactly("Unable to verify JWT token. " + message);
110112

111113
}
@@ -116,22 +118,22 @@ void verifyJwt_OnInvalidClaims_fails() throws Exception {
116118
var message = FAKER.lorem().sentence();
117119
when(jwt.verify(any())).thenReturn(true);
118120
when(jwt.getJWTClaimsSet()).thenThrow(new ParseException(message, 0));
119-
assertThat(VerifiableCredentialFactory.verify(jwt, publicKey, "test-audience").getFailureMessages())
121+
assertThat(verify(jwt, publicKey, "test-audience").getFailureMessages())
120122
.containsExactly("Error verifying JWT token. The payload must represent a valid JSON object and a JWT claims set. " + message);
121123
}
122124

123125
@ParameterizedTest(name = "{2}")
124126
@ArgumentsSource(ClaimsArgs.class)
125127
void verifyJwt_OnClaims(UnaryOperator<JWTClaimsSet.Builder> builderOperator, boolean expectSuccess, String ignoredName) throws Exception {
126-
var vc = VerifiableCredentialFactory.create(privateKey, "test-connector", "test-audience", clock);
128+
var vc = create(privateKey, "test-issuer", "test-subject", "test-audience", clock);
127129

128130
var claimsSetBuilder = new JWTClaimsSet.Builder(vc.getJWTClaimsSet());
129131
var claimsSet = builderOperator.apply(claimsSetBuilder).build();
130132

131133
var jwt = new SignedJWT(vc.getHeader(), claimsSet);
132134
jwt.sign(privateKey.signer());
133135

134-
var result = VerifiableCredentialFactory.verify(jwt, publicKey, "test-audience");
136+
var result = verify(jwt, publicKey, "test-audience");
135137
assertThat(result.succeeded()).isEqualTo(expectSuccess);
136138
if (!expectSuccess) {
137139
assertThat(result.getFailureMessages())
@@ -149,8 +151,7 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
149151
jwtCase(b -> b.audience(List.of("https://otherserver.com")), false, "wrong audience"),
150152
jwtCase(b -> b.audience(List.of("test-audience")), true, "expected audience"), // sanity check
151153
jwtCase(b -> b.subject(null), false, "empty subject"),
152-
jwtCase(b -> b.subject("other-subject"), false, "wrong subject"),
153-
jwtCase(b -> b.subject("verifiable-credential"), true, "expected subject"), // sanity check
154+
jwtCase(b -> b.subject("a-subject"), true, "expected subject"), // sanity check
154155
jwtCase(b -> b.issuer(null), false, "empty issuer"),
155156
jwtCase(b -> b.issuer("other-issuer"), true, "other issuer"),
156157
jwtCase(b -> b.expirationTime(null), false, "empty expiration"),

0 commit comments

Comments
 (0)