diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/controller/SecureTokenServiceApiController.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/controller/SecureTokenServiceApiController.java
index db5be52e480..1594d2d6c1e 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/controller/SecureTokenServiceApiController.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/controller/SecureTokenServiceApiController.java
@@ -67,6 +67,7 @@ private StsClientTokenAdditionalParams additionalParams(StsTokenRequest request)
.audience(request.getAudience())
.accessToken(request.getAccessToken())
.bearerAccessScope(request.getBearerAccessScope())
+ .bearerAccessAlias(request.getBearerAccessAlias())
.build();
}
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/model/StsTokenRequest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/model/StsTokenRequest.java
index 0a2ccecee4d..88f8e546f09 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/model/StsTokenRequest.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-api/src/main/java/org/eclipse/edc/connector/api/sts/model/StsTokenRequest.java
@@ -27,7 +27,7 @@
*
clientSecret: Authorization secret for the client/
* audience: Audience according to the spec.
* bearerAccessScope: Space-delimited scopes to be included in the access_token claim.
- * bearerAccessAlias: Alias to be use in the sub of the VP access token (default is audience).
+ * bearerAccessAlias: Alias to be used in the sub of the VP access token (default is audience).
* accessToken: VP/VC Access Token to be included as access_token claim.
* grantType: Type of grant. Must be client_credentials.
*
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/service/StsClientTokenGeneratorServiceImpl.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/service/StsClientTokenGeneratorServiceImpl.java
index 74487bdd844..088d59d786f 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/service/StsClientTokenGeneratorServiceImpl.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/service/StsClientTokenGeneratorServiceImpl.java
@@ -29,6 +29,8 @@
import java.util.Optional;
import java.util.function.Function;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_ALIAS;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.CLIENT_ID;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER;
@@ -36,8 +38,10 @@
public class StsClientTokenGeneratorServiceImpl implements StsClientTokenGeneratorService {
- public static final String ACCESS_TOKEN_CLAIM = "access_token";
-
+ private static final Map> CLAIM_MAPPERS = Map.of(
+ ACCESS_TOKEN, StsClientTokenAdditionalParams::getAccessToken,
+ BEARER_ACCESS_ALIAS, StsClientTokenAdditionalParams::getBearerAccessAlias);
+
private final long tokenExpiration;
private final StsTokenGenerationProvider tokenGenerationProvider;
private final Clock clock;
@@ -46,7 +50,6 @@ public StsClientTokenGeneratorServiceImpl(StsTokenGenerationProvider tokenGenera
this.tokenGenerationProvider = tokenGenerationProvider;
this.clock = clock;
this.tokenExpiration = tokenExpiration;
-
}
@Override
@@ -59,9 +62,12 @@ public ServiceResult tokenFor(StsClient client, StsClientTo
AUDIENCE, additionalParams.getAudience(),
CLIENT_ID, client.getClientId());
- var claims = Optional.ofNullable(additionalParams.getAccessToken())
- .map(enrichClaims(initialClaims))
- .orElse(initialClaims);
+ var claims = CLAIM_MAPPERS.entrySet().stream()
+ .filter(entry -> entry.getValue().apply(additionalParams) != null)
+ .reduce(initialClaims, (accumulator, entity) ->
+ Optional.ofNullable(entity.getValue().apply(additionalParams))
+ .map(enrichClaimsWith(accumulator, entity.getKey()))
+ .orElse(accumulator), (a, b) -> b);
var tokenResult = embeddedTokenGenerator.createToken(claims, additionalParams.getBearerAccessScope())
.map(this::enrichWithExpiration);
@@ -80,10 +86,10 @@ private TokenRepresentation enrichWithExpiration(TokenRepresentation tokenRepres
.build();
}
- private Function> enrichClaims(Map claims) {
- return (token) -> {
+ private Function> enrichClaimsWith(Map claims, String claim) {
+ return (claimValue) -> {
var newClaims = new HashMap<>(claims);
- newClaims.put(ACCESS_TOKEN_CLAIM, token);
+ newClaims.put(claim, claimValue);
return Collections.unmodifiableMap(newClaims);
};
}
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/StsClientTokenIssuanceIntegrationTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/StsClientTokenIssuanceIntegrationTest.java
index 0e2ee69961c..c3eb718b1c8 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/StsClientTokenIssuanceIntegrationTest.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/core/defaults/StsClientTokenIssuanceIntegrationTest.java
@@ -38,7 +38,9 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.edc.iam.identitytrust.sts.store.fixtures.TestFunctions.createClientBuilder;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
+import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.CLIENT_ID;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.EXPIRATION_TIME;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUED_AT;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER;
@@ -94,7 +96,7 @@ void authenticateAndGenerateToken() throws Exception {
.containsEntry(ISSUER, id)
.containsEntry(SUBJECT, id)
.containsEntry(AUDIENCE, List.of(audience))
- .containsEntry("client_id", clientId)
+ .containsEntry(CLIENT_ID, clientId)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
}
@@ -127,12 +129,11 @@ void authenticateAndGenerateToken_withBearerAccessScope() throws Exception {
.containsEntry(ISSUER, id)
.containsEntry(SUBJECT, id)
.containsEntry(AUDIENCE, List.of(audience))
- .containsEntry("client_id", clientId)
+ .containsEntry(CLIENT_ID, clientId)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT, "access_token");
}
-
@Test
void authenticateAndGenerateToken_withAccessToken() throws Exception {
var id = "id";
@@ -161,13 +162,12 @@ void authenticateAndGenerateToken_withAccessToken() throws Exception {
.containsEntry(ISSUER, id)
.containsEntry(SUBJECT, id)
.containsEntry(AUDIENCE, List.of(audience))
- .containsEntry("client_id", clientId)
- .containsEntry("access_token", accessToken)
+ .containsEntry(CLIENT_ID, clientId)
+ .containsEntry(ACCESS_TOKEN, accessToken)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
}
-
/**
* Load content from a resource file.
*/
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/README.md b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/README.md
new file mode 100644
index 00000000000..cf0af737241
--- /dev/null
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/README.md
@@ -0,0 +1,37 @@
+# Embedded Secure Token Service (STS) Extension
+
+## Overview
+
+This module implements the `SecureTokenService` spi, which will be used for generating the Self-Issued ID Token
+in the `IATP` protocol flow. This is an embeddable implementation, which can be used in the same process of
+the EDC control-plane runtime.
+
+## Self-Issued ID Token Contents
+
+As outlined in the [IATP](https://github.com/eclipse-tractusx/identity-trust/blob/main/specifications/M1/identity.protocol.base.md#41-self-issued-id-token-contents) spec
+the token includes the following claims:
+
+- The `iss` and `sub` claims MUST be equal and set to the bearer's (participant's) DID.
+- The `sub_jwk` claim is not used
+- The `aud` set to the `participant_id` of the relying party (RP)
+- The `client_id` set to the `participant_id` of the consumer
+- The `jti` claim that is used to mitigate against replay attacks
+- The `exp` expiration time of the token
+- The `access_token` VP Access Token (Optional)
+
+Additionally, when generating the Self-Issued ID Token the `bearerAccessScope` parameter is passed the additional claim
+`access_token` claim is added.
+
+## VP Access Token format
+
+The `IATP` protocol does not specify the format of the VP Access Token, which it's up to the specific STS implementation.
+In this implementation the VP access token is still a JWT token with the following claims:
+
+- The `iss` is the same of the SI token (participant's DID)
+- The `sub` set to the `participant_id`/`alias (DID)` of the relying party (RP)
+- The `aud` set to the `participant_id` of the participant
+- The `jti` claim that is used to mitigate against replay attacks
+- The `exp` expiration time of the token
+
+`CredentialService` implementors, should verify that the `sub` of the Self-Issued ID token and the `sub` of
+the VP access token matches.
\ No newline at end of file
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java
index 6886403f3cd..b9f4bcf64e5 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/main/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenService.java
@@ -30,8 +30,11 @@
import static java.lang.String.format;
import static java.util.Optional.ofNullable;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_ALIAS;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER;
+import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SCOPE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT;
import static org.eclipse.edc.spi.result.Result.failure;
import static org.eclipse.edc.spi.result.Result.success;
@@ -43,9 +46,6 @@
*/
public class EmbeddedSecureTokenService implements SecureTokenService {
- public static final String SCOPE_CLAIM = "scope";
- public static final String ACCESS_TOKEN_CLAIM = "access_token";
- public static final String BEARER_ACCESS_ALIAS_CLAIM = "bearer_access_alias";
private static final List ACCESS_TOKEN_INHERITED_CLAIMS = List.of(ISSUER);
private final TokenGenerationService tokenGenerationService;
private final Clock clock;
@@ -69,16 +69,16 @@ public Result createToken(Map claims, @Null
private Result createAndAcceptAccessToken(Map claims, String scope, BiConsumer consumer) {
return createAccessToken(claims, scope)
.compose(tokenRepresentation -> success(tokenRepresentation.getToken()))
- .onSuccess(withClaim(ACCESS_TOKEN_CLAIM, consumer))
+ .onSuccess(withClaim(ACCESS_TOKEN, consumer))
.mapTo();
}
private Result createAccessToken(Map claims, String bearerAccessScope) {
var accessTokenClaims = new HashMap<>(accessTokenInheritedClaims(claims));
- accessTokenClaims.put(SCOPE_CLAIM, bearerAccessScope);
+ accessTokenClaims.put(SCOPE, bearerAccessScope);
return addClaim(claims, ISSUER, withClaim(AUDIENCE, accessTokenClaims::put))
.compose(v -> addClaim(claims, AUDIENCE, withClaim(SUBJECT, accessTokenClaims::put)))
- .compose(v -> addOptionalClaim(claims, BEARER_ACCESS_ALIAS_CLAIM, withClaim(SUBJECT, accessTokenClaims::put)))
+ .compose(v -> addOptionalClaim(claims, BEARER_ACCESS_ALIAS, withClaim(SUBJECT, accessTokenClaims::put)))
.compose(v -> tokenGenerationService.generate(new SelfIssuedTokenDecorator(accessTokenClaims, clock, validity)));
}
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceIntegrationTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceIntegrationTest.java
index a0558e32066..38717872f0c 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceIntegrationTest.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceIntegrationTest.java
@@ -40,15 +40,15 @@
import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
-import static org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService.ACCESS_TOKEN_CLAIM;
-import static org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService.BEARER_ACCESS_ALIAS_CLAIM;
-import static org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService.SCOPE_CLAIM;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_ALIAS;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.EXPIRATION_TIME;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUED_AT;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.JWT_ID;
+import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SCOPE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT;
@@ -83,7 +83,7 @@ void createToken_withoutBearerAccessScope() {
assertThat(jwt.getJWTClaimsSet().getClaims())
.containsEntry(ISSUER, issuer)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT)
- .doesNotContainKey(ACCESS_TOKEN_CLAIM);
+ .doesNotContainKey(ACCESS_TOKEN);
});
}
@@ -104,7 +104,7 @@ void createToken_withBearerAccessScope() {
assertThat(jwt.getJWTClaimsSet().getClaims())
.containsEntry(ISSUER, issuer)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT)
- .extractingByKey(ACCESS_TOKEN_CLAIM, as(STRING))
+ .extractingByKey(ACCESS_TOKEN, as(STRING))
.satisfies(accessToken -> {
var accessTokenJwt = SignedJWT.parse(accessToken);
assertThat(accessTokenJwt.verify(createVerifier(accessTokenJwt.getHeader(), keyPair.getPublic()))).isTrue();
@@ -112,7 +112,7 @@ void createToken_withBearerAccessScope() {
.containsEntry(ISSUER, issuer)
.containsEntry(SUBJECT, audience)
.containsEntry(AUDIENCE, List.of(issuer))
- .containsEntry(SCOPE_CLAIM, scopes)
+ .containsEntry(SCOPE, scopes)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
});
});
@@ -124,7 +124,7 @@ void createToken_withBearerAccessAlias() {
var issuer = "testIssuer";
var audience = "audience";
var bearerAccessAlias = "alias";
- var claims = Map.of(ISSUER, issuer, AUDIENCE, audience, BEARER_ACCESS_ALIAS_CLAIM, bearerAccessAlias);
+ var claims = Map.of(ISSUER, issuer, AUDIENCE, audience, BEARER_ACCESS_ALIAS, bearerAccessAlias);
var tokenResult = secureTokenService.createToken(claims, scopes);
assertThat(tokenResult).isSucceeded()
@@ -135,7 +135,7 @@ void createToken_withBearerAccessAlias() {
assertThat(jwt.getJWTClaimsSet().getClaims())
.containsEntry(ISSUER, issuer)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT)
- .extractingByKey(ACCESS_TOKEN_CLAIM, as(STRING))
+ .extractingByKey(ACCESS_TOKEN, as(STRING))
.satisfies(accessToken -> {
var accessTokenJwt = SignedJWT.parse(accessToken);
assertThat(accessTokenJwt.verify(createVerifier(accessTokenJwt.getHeader(), keyPair.getPublic()))).isTrue();
@@ -143,13 +143,12 @@ void createToken_withBearerAccessAlias() {
.containsEntry(ISSUER, issuer)
.containsEntry(SUBJECT, bearerAccessAlias)
.containsEntry(AUDIENCE, List.of(issuer))
- .containsEntry(SCOPE_CLAIM, scopes)
+ .containsEntry(SCOPE, scopes)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
});
});
}
-
-
+
@ParameterizedTest
@ArgumentsSource(ClaimsArguments.class)
void createToken_shouldFail_withMissingClaims(Map claims) {
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientConfigurationExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientConfigurationExtension.java
index e45dd6324e0..a772378929f 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientConfigurationExtension.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientConfigurationExtension.java
@@ -41,8 +41,7 @@ public class StsRemoteClientConfigurationExtension implements ServiceExtension {
public static final String CLIENT_SECRET_ALIAS = "edc.iam.sts.oauth.client.secret.alias";
protected static final String NAME = "Sts remote client configuration extension";
-
-
+
@Inject
private Vault vault;
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientExtension.java
index 5c71d092d2d..223056741c9 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientExtension.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/core/StsRemoteClientExtension.java
@@ -28,11 +28,9 @@
*/
@Extension(StsRemoteClientExtension.NAME)
public class StsRemoteClientExtension implements ServiceExtension {
-
-
+
protected static final String NAME = "Sts remote client configuration extension";
-
@Inject
private StsRemoteClientConfiguration clientConfiguration;
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenService.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenService.java
index 848ba46d166..5bfdb943260 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenService.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenService.java
@@ -26,19 +26,21 @@
import java.util.Map;
import java.util.stream.Collectors;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_ALIAS;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_SCOPE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
public class RemoteSecureTokenService implements SecureTokenService {
public static final String GRANT_TYPE = "client_credentials";
- public static final String ACCESS_TOKEN_PARAM = "access_token";
public static final String AUDIENCE_PARAM = "audience";
- public static final String BEARER_ACCESS_ALIAS_PARAM = "bearer_access_alias";
- public static final String BEARER_ACCESS_SCOPE_PARAM = "bearer_access_scope";
+
private static final Map CLAIM_MAPPING = Map.of(
AUDIENCE, AUDIENCE_PARAM,
- BEARER_ACCESS_ALIAS_PARAM, BEARER_ACCESS_ALIAS_PARAM,
- ACCESS_TOKEN_PARAM, ACCESS_TOKEN_PARAM);
+ BEARER_ACCESS_ALIAS, BEARER_ACCESS_ALIAS,
+ ACCESS_TOKEN, ACCESS_TOKEN);
+
private final Oauth2Client oauth2Client;
private final StsRemoteClientConfiguration configuration;
@@ -59,18 +61,14 @@ private Oauth2CredentialsRequest createRequest(Map claims, @Null
.clientId(configuration.getClientId())
.clientSecret(configuration.getClientSecret())
.grantType(GRANT_TYPE);
-
- if (configuration.getScope() != null) {
- builder.scope(configuration.getScope());
- }
-
+
var additionalParams = claims.entrySet().stream()
.filter(entry -> CLAIM_MAPPING.containsKey(entry.getKey()))
.map(entry -> Map.entry(CLAIM_MAPPING.get(entry.getKey()), entry.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (bearerAccessScope != null) {
- additionalParams.put(BEARER_ACCESS_SCOPE_PARAM, bearerAccessScope);
+ additionalParams.put(BEARER_ACCESS_SCOPE, bearerAccessScope);
}
builder.params(additionalParams);
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/StsRemoteClientConfiguration.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/StsRemoteClientConfiguration.java
index bc78b484c2b..c789b903cac 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/StsRemoteClientConfiguration.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/main/java/org/eclipse/edc/iam/identitytrust/sts/remote/StsRemoteClientConfiguration.java
@@ -20,15 +20,10 @@
* Configuration of the OAuth2 client
*/
public class StsRemoteClientConfiguration {
+
private String tokenUrl;
private String clientId;
-
private String clientSecret;
- private String scope;
-
- public String getScope() {
- return scope;
- }
public String getClientId() {
return clientId;
@@ -62,11 +57,6 @@ public Builder clientId(String clientId) {
return this;
}
- public Builder scope(String scope) {
- configuration.scope = scope;
- return this;
- }
-
public Builder clientSecret(String clientSecret) {
configuration.clientSecret = clientSecret;
return this;
diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/test/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenServiceTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/test/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenServiceTest.java
index bb8b773704b..29745d6e94e 100644
--- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/test/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenServiceTest.java
+++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-remote/src/test/java/org/eclipse/edc/iam/identitytrust/sts/remote/RemoteSecureTokenServiceTest.java
@@ -25,11 +25,11 @@
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.eclipse.edc.iam.identitytrust.sts.remote.RemoteSecureTokenService.ACCESS_TOKEN_PARAM;
import static org.eclipse.edc.iam.identitytrust.sts.remote.RemoteSecureTokenService.AUDIENCE_PARAM;
-import static org.eclipse.edc.iam.identitytrust.sts.remote.RemoteSecureTokenService.BEARER_ACCESS_ALIAS_PARAM;
-import static org.eclipse.edc.iam.identitytrust.sts.remote.RemoteSecureTokenService.BEARER_ACCESS_SCOPE_PARAM;
import static org.eclipse.edc.iam.identitytrust.sts.remote.RemoteSecureTokenService.GRANT_TYPE;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_ALIAS;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_SCOPE;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
import static org.mockito.ArgumentMatchers.any;
@@ -44,6 +44,7 @@ public class RemoteSecureTokenServiceTest {
.clientSecret("secret")
.tokenUrl("url")
.build();
+
private final Oauth2Client oauth2Client = mock();
private RemoteSecureTokenService secureTokenService;
@@ -88,7 +89,7 @@ void createToken_withAccessScope() {
assertThat(request.getClientSecret()).isEqualTo(configuration.getClientSecret());
assertThat(request.getParams())
.containsEntry(AUDIENCE_PARAM, audience)
- .containsEntry(BEARER_ACCESS_SCOPE_PARAM, bearerAccessScope);
+ .containsEntry(BEARER_ACCESS_SCOPE, bearerAccessScope);
});
}
@@ -97,7 +98,7 @@ void createToken_withAccessToken() {
var audience = "aud";
var accessToken = "accessToken";
when(oauth2Client.requestToken(any())).thenReturn(Result.success(TokenRepresentation.Builder.newInstance().build()));
- assertThat(secureTokenService.createToken(Map.of(AUDIENCE, audience, ACCESS_TOKEN_PARAM, accessToken), null)).isSucceeded();
+ assertThat(secureTokenService.createToken(Map.of(AUDIENCE, audience, ACCESS_TOKEN, accessToken), null)).isSucceeded();
var captor = ArgumentCaptor.forClass(SharedSecretOauth2CredentialsRequest.class);
verify(oauth2Client).requestToken(captor.capture());
@@ -109,7 +110,7 @@ void createToken_withAccessToken() {
assertThat(request.getClientSecret()).isEqualTo(configuration.getClientSecret());
assertThat(request.getParams())
.containsEntry(AUDIENCE_PARAM, audience)
- .containsEntry(ACCESS_TOKEN_PARAM, accessToken);
+ .containsEntry(ACCESS_TOKEN, accessToken);
});
}
@@ -123,7 +124,7 @@ void createToken_withBearerAccessTokenAlias() {
var claims = Map.of(
AUDIENCE, audience,
- BEARER_ACCESS_ALIAS_PARAM, bearerAccessAlias);
+ BEARER_ACCESS_ALIAS, bearerAccessAlias);
assertThat(secureTokenService.createToken(claims, bearerAccessScope)).isSucceeded();
@@ -137,8 +138,8 @@ void createToken_withBearerAccessTokenAlias() {
assertThat(request.getClientSecret()).isEqualTo(configuration.getClientSecret());
assertThat(request.getParams())
.containsEntry(AUDIENCE_PARAM, audience)
- .containsEntry(BEARER_ACCESS_ALIAS_PARAM, bearerAccessAlias)
- .containsEntry(BEARER_ACCESS_SCOPE_PARAM, bearerAccessScope);
+ .containsEntry(BEARER_ACCESS_ALIAS, bearerAccessAlias)
+ .containsEntry(BEARER_ACCESS_SCOPE, bearerAccessScope);
});
}
}
diff --git a/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/SelfIssuedTokenConstants.java b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/SelfIssuedTokenConstants.java
new file mode 100644
index 00000000000..7bfc072e31a
--- /dev/null
+++ b/spi/common/identity-trust-spi/src/main/java/org/eclipse/edc/identitytrust/SelfIssuedTokenConstants.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.identitytrust;
+
+/**
+ * Constants for the Self-Issued ID Token
+ */
+public final class SelfIssuedTokenConstants {
+
+ /**
+ * VP access token claim
+ */
+ public static final String ACCESS_TOKEN = "access_token";
+
+ /**
+ * Alias to be used in the sub of the VP access token
+ */
+ public static final String BEARER_ACCESS_ALIAS = "bearer_access_alias";
+
+ /**
+ * Scopes to be encoded in the VP access token
+ */
+ public static final String BEARER_ACCESS_SCOPE = "bearer_access_scope";
+
+ private SelfIssuedTokenConstants() {
+
+ }
+}
diff --git a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/model/StsClientTokenAdditionalParams.java b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/model/StsClientTokenAdditionalParams.java
index 631beef5ee4..255839230a9 100644
--- a/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/model/StsClientTokenAdditionalParams.java
+++ b/spi/common/identity-trust-sts-spi/src/main/java/org/eclipse/edc/iam/identitytrust/sts/model/StsClientTokenAdditionalParams.java
@@ -25,6 +25,9 @@
public class StsClientTokenAdditionalParams {
private String bearerAccessScope;
+
+ private String bearerAccessAlias;
+
private String audience;
private String accessToken;
@@ -44,6 +47,10 @@ public String getAccessToken() {
return accessToken;
}
+ public String getBearerAccessAlias() {
+ return bearerAccessAlias;
+ }
+
public static class Builder {
private final StsClientTokenAdditionalParams params;
@@ -67,6 +74,11 @@ public Builder bearerAccessScope(String bearerAccessScope) {
return this;
}
+ public Builder bearerAccessAlias(String bearerAccessAlias) {
+ params.bearerAccessAlias = bearerAccessAlias;
+ return this;
+ }
+
public Builder accessToken(String accessToken) {
params.accessToken = accessToken;
return this;
diff --git a/spi/common/jwt-spi/src/main/java/org/eclipse/edc/jwt/spi/JwtRegisteredClaimNames.java b/spi/common/jwt-spi/src/main/java/org/eclipse/edc/jwt/spi/JwtRegisteredClaimNames.java
index 3b8b751ed42..0d65c2ee3a0 100644
--- a/spi/common/jwt-spi/src/main/java/org/eclipse/edc/jwt/spi/JwtRegisteredClaimNames.java
+++ b/spi/common/jwt-spi/src/main/java/org/eclipse/edc/jwt/spi/JwtRegisteredClaimNames.java
@@ -76,6 +76,13 @@ public final class JwtRegisteredClaimNames {
*/
public static final String CLIENT_ID = "client_id";
+
+ /**
+ * "scope" (Scopes) Claim
+ *
+ * @see RFC 8693 "scope" (Scopes) Claim
+ */
+ public static final String SCOPE = "scope";
private JwtRegisteredClaimNames() {
}
diff --git a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/RemoteStsEndToEndTest.java b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/RemoteStsEndToEndTest.java
index 8c623f0bac3..f0d8a4ab53d 100644
--- a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/RemoteStsEndToEndTest.java
+++ b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/RemoteStsEndToEndTest.java
@@ -14,30 +14,25 @@
package org.eclipse.edc.test.e2e.sts.api;
-import com.nimbusds.jwt.SignedJWT;
-import org.eclipse.edc.iam.identitytrust.sts.model.StsClient;
import org.eclipse.edc.iam.identitytrust.sts.remote.RemoteSecureTokenService;
import org.eclipse.edc.iam.identitytrust.sts.remote.StsRemoteClientConfiguration;
-import org.eclipse.edc.iam.identitytrust.sts.store.StsClientStore;
import org.eclipse.edc.iam.oauth2.client.Oauth2ClientImpl;
import org.eclipse.edc.junit.annotations.EndToEndTest;
import org.eclipse.edc.junit.extensions.EdcRuntimeExtension;
import org.eclipse.edc.spi.iam.TokenRepresentation;
import org.eclipse.edc.spi.result.Failure;
-import org.eclipse.edc.spi.security.Vault;
import org.eclipse.edc.spi.types.TypeManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
-import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.eclipse.edc.iam.identitytrust.sts.store.fixtures.TestFunctions.createClient;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.BEARER_ACCESS_ALIAS;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort;
import static org.eclipse.edc.junit.testfixtures.TestUtils.testHttpClient;
@@ -50,7 +45,7 @@
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT;
@EndToEndTest
-public class RemoteStsEndToEndTest {
+public class RemoteStsEndToEndTest extends StsEndToEndTestBase {
public static final int PORT = getFreePort();
public static final String STS_TOKEN_PATH = "http://localhost:" + PORT + "/sts/token";
@@ -84,7 +79,7 @@ void setup() {
@Test
void requestToken() {
var audience = "audience";
- var params = Map.of("aud", audience);
+ var params = Map.of(AUDIENCE, audience);
var client = initClient(config.getClientId(), config.getClientSecret());
@@ -105,10 +100,11 @@ void requestToken() {
@Test
- void requestToken_withBearerScope() {
+ void requestToken_withBearerScopeAndAlias() {
var audience = "audience";
var bearerAccessScope = "org.test.Member:read org.test.GoldMember:read";
- var params = Map.of("aud", audience);
+ var bearerAccessAlias = "alias";
+ var params = Map.of(AUDIENCE, audience, BEARER_ACCESS_ALIAS, bearerAccessAlias);
var client = initClient(config.getClientId(), config.getClientSecret());
@@ -123,10 +119,10 @@ void requestToken_withBearerScope() {
.containsEntry(AUDIENCE, List.of(audience))
.containsEntry(CLIENT_ID, client.getClientId())
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT)
- .hasEntrySatisfying("access_token", (accessToken) -> {
+ .hasEntrySatisfying(ACCESS_TOKEN, (accessToken) -> {
assertThat(parseClaims((String) accessToken))
.containsEntry(ISSUER, client.getId())
- .containsEntry(SUBJECT, audience)
+ .containsEntry(SUBJECT, bearerAccessAlias)
.containsEntry(AUDIENCE, List.of(client.getClientId()))
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
@@ -140,13 +136,11 @@ void requestToken_withAttachedAccessToken() {
var audience = "audience";
var accessToken = "test_token";
var params = Map.of(
- "aud", audience,
- "access_token", accessToken);
-
+ AUDIENCE, audience,
+ ACCESS_TOKEN, accessToken);
var client = initClient(config.getClientId(), config.getClientSecret());
-
assertThat(remoteSecureTokenService.createToken(params, null))
.isSucceeded()
.extracting(TokenRepresentation::getToken)
@@ -156,7 +150,7 @@ void requestToken_withAttachedAccessToken() {
.containsEntry(SUBJECT, client.getId())
.containsEntry(AUDIENCE, List.of(audience))
.containsEntry(CLIENT_ID, client.getClientId())
- .containsEntry("access_token", accessToken)
+ .containsEntry(ACCESS_TOKEN, accessToken)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
});
}
@@ -164,52 +158,17 @@ void requestToken_withAttachedAccessToken() {
@Test
void requestToken_shouldReturnError_whenClientNotFound() {
var audience = "audience";
- var params = Map.of("aud", audience);
+ var params = Map.of(AUDIENCE, audience);
assertThat(remoteSecureTokenService.createToken(params, null)).isFailed()
.extracting(Failure::getFailureDetail)
.satisfies(failure -> assertThat(failure).contains("Invalid client"));
}
-
- private StsClient initClient(String clientId, String clientSecret) {
- var store = getClientStore();
- var vault = getVault();
- var clientSecretAlias = "client_secret_alias";
- var client = createClient(clientId, clientSecretAlias);
-
-
- vault.storeSecret(clientSecretAlias, clientSecret);
- vault.storeSecret(client.getPrivateKeyAlias(), loadResourceFile("ec-privatekey.pem"));
- store.create(client);
-
- return client;
- }
- private StsClientStore getClientStore() {
- return sts.getContext().getService(StsClientStore.class);
+ @Override
+ protected EdcRuntimeExtension getRuntime() {
+ return sts;
}
- private Vault getVault() {
- return sts.getContext().getService(Vault.class);
- }
-
- /**
- * Load content from a resource file.
- */
- private String loadResourceFile(String file) {
- try (var resourceAsStream = RemoteStsEndToEndTest.class.getClassLoader().getResourceAsStream(file)) {
- return new String(Objects.requireNonNull(resourceAsStream).readAllBytes());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private Map parseClaims(String token) {
- try {
- return SignedJWT.parse(token).getJWTClaimsSet().getClaims();
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- }
}
diff --git a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsApiEndToEndTest.java b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsApiEndToEndTest.java
index 27582d433d6..229f637dc62 100644
--- a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsApiEndToEndTest.java
+++ b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsApiEndToEndTest.java
@@ -14,14 +14,10 @@
package org.eclipse.edc.test.e2e.sts.api;
-import com.nimbusds.jwt.SignedJWT;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
-import org.eclipse.edc.iam.identitytrust.sts.model.StsClient;
-import org.eclipse.edc.iam.identitytrust.sts.store.StsClientStore;
import org.eclipse.edc.junit.annotations.EndToEndTest;
import org.eclipse.edc.junit.extensions.EdcRuntimeExtension;
-import org.eclipse.edc.spi.security.Vault;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -30,12 +26,11 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import static io.restassured.RestAssured.given;
import static io.restassured.http.ContentType.JSON;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.eclipse.edc.iam.identitytrust.sts.store.fixtures.TestFunctions.createClient;
+import static org.eclipse.edc.identitytrust.SelfIssuedTokenConstants.ACCESS_TOKEN;
import static org.eclipse.edc.junit.testfixtures.TestUtils.getFreePort;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.CLIENT_ID;
@@ -48,7 +43,7 @@
import static org.hamcrest.Matchers.notNullValue;
@EndToEndTest
-public class StsApiEndToEndTest {
+public class StsApiEndToEndTest extends StsEndToEndTestBase {
public static final int PORT = getFreePort();
public static final String BASE_STS = "http://localhost:" + PORT + "/sts";
@@ -90,9 +85,7 @@ void requestToken() throws ParseException {
.body()
.jsonPath().getString("access_token");
- var jwt = SignedJWT.parse(token);
-
- assertThat(jwt.getJWTClaimsSet().getClaims())
+ assertThat(parseClaims(token))
.containsEntry(ISSUER, client.getId())
.containsEntry(SUBJECT, client.getId())
.containsEntry(AUDIENCE, List.of(audience))
@@ -127,27 +120,18 @@ void requestToken_withBearerScope() throws ParseException {
.jsonPath().getString("access_token");
- var jwt = SignedJWT.parse(token);
-
- assertThat(jwt.getJWTClaimsSet().getClaims())
+ assertThat(parseClaims(token))
.containsEntry(ISSUER, client.getId())
.containsEntry(SUBJECT, client.getId())
.containsEntry(AUDIENCE, List.of(audience))
.containsEntry(CLIENT_ID, client.getClientId())
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT)
- .hasEntrySatisfying("access_token", (accessToken) -> {
- try {
- var accessTokenJwt = SignedJWT.parse(((String) accessToken));
-
- assertThat(accessTokenJwt.getJWTClaimsSet().getClaims())
- .containsEntry(ISSUER, client.getId())
- .containsEntry(SUBJECT, audience)
- .containsEntry(AUDIENCE, List.of(client.getClientId()))
- .containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
-
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
+ .hasEntrySatisfying(ACCESS_TOKEN, (accessToken) -> {
+ assertThat(parseClaims((String) accessToken))
+ .containsEntry(ISSUER, client.getId())
+ .containsEntry(SUBJECT, audience)
+ .containsEntry(AUDIENCE, List.of(client.getClientId()))
+ .containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
});
}
@@ -175,15 +159,13 @@ void requestToken_withAttachedAccessScope() throws IOException, ParseException {
.body()
.jsonPath().getString("access_token");
-
- var jwt = SignedJWT.parse(token);
-
- assertThat(jwt.getJWTClaimsSet().getClaims())
+
+ assertThat(parseClaims(token))
.containsEntry(ISSUER, client.getId())
.containsEntry(SUBJECT, client.getId())
.containsEntry(AUDIENCE, List.of(audience))
.containsEntry(CLIENT_ID, client.getClientId())
- .containsEntry("access_token", accessToken)
+ .containsEntry(ACCESS_TOKEN, accessToken)
.containsKeys(JWT_ID, EXPIRATION_TIME, ISSUED_AT);
}
@@ -220,37 +202,9 @@ protected RequestSpecification baseRequest() {
.when();
}
- private StsClient initClient(String clientSecret) {
- var store = getClientStore();
- var vault = getVault();
- var clientId = "client_id";
- var clientSecretAlias = "client_secret_alias";
- var client = createClient(clientId, clientSecretAlias);
-
-
- vault.storeSecret(clientSecretAlias, clientSecret);
- vault.storeSecret(client.getPrivateKeyAlias(), loadResourceFile("ec-privatekey.pem"));
- store.create(client);
-
- return client;
- }
-
- private StsClientStore getClientStore() {
- return sts.getContext().getService(StsClientStore.class);
+ @Override
+ protected EdcRuntimeExtension getRuntime() {
+ return sts;
}
- private Vault getVault() {
- return sts.getContext().getService(Vault.class);
- }
-
- /**
- * Load content from a resource file.
- */
- private String loadResourceFile(String file) {
- try (var resourceAsStream = StsApiEndToEndTest.class.getClassLoader().getResourceAsStream(file)) {
- return new String(Objects.requireNonNull(resourceAsStream).readAllBytes());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
}
diff --git a/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java
new file mode 100644
index 00000000000..f1722342e97
--- /dev/null
+++ b/system-tests/sts-api/sts-api-test-runner/src/test/java/org/eclipse/edc/test/e2e/sts/api/StsEndToEndTestBase.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Contributors:
+ * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
+ *
+ */
+
+package org.eclipse.edc.test.e2e.sts.api;
+
+import com.nimbusds.jwt.SignedJWT;
+import org.eclipse.edc.iam.identitytrust.sts.model.StsClient;
+import org.eclipse.edc.iam.identitytrust.sts.store.StsClientStore;
+import org.eclipse.edc.junit.extensions.EdcRuntimeExtension;
+import org.eclipse.edc.spi.security.Vault;
+
+import java.text.ParseException;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import static org.eclipse.edc.iam.identitytrust.sts.store.fixtures.TestFunctions.createClient;
+
+/**
+ * Base class for STS E2E tests
+ */
+public abstract class StsEndToEndTestBase {
+
+ protected abstract EdcRuntimeExtension getRuntime();
+
+ protected StsClient initClient(String clientId, String clientSecret) {
+ var store = getClientStore();
+ var vault = getVault();
+ var clientSecretAlias = "client_secret_alias";
+ var client = createClient(clientId, clientSecretAlias);
+
+ vault.storeSecret(clientSecretAlias, clientSecret);
+ vault.storeSecret(client.getPrivateKeyAlias(), loadResourceFile("ec-privatekey.pem"));
+ store.create(client);
+
+ return client;
+ }
+
+ protected StsClient initClient(String clientSecret) {
+ return initClient(UUID.randomUUID().toString(), clientSecret);
+ }
+
+ protected Map parseClaims(String token) {
+ try {
+ return SignedJWT.parse(token).getJWTClaimsSet().getClaims();
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private StsClientStore getClientStore() {
+ return getRuntime().getContext().getService(StsClientStore.class);
+ }
+
+ private Vault getVault() {
+ return getRuntime().getContext().getService(Vault.class);
+ }
+
+ /**
+ * Load content from a resource file.
+ */
+ private String loadResourceFile(String file) {
+ try (var resourceAsStream = RemoteStsEndToEndTest.class.getClassLoader().getResourceAsStream(file)) {
+ return new String(Objects.requireNonNull(resourceAsStream).readAllBytes());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}