From 7f51a3d6c2e7d2548b6be040221a11875513e320 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger <43503240+paullatzelsperger@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:29:55 +0200 Subject: [PATCH] refactor: TokenGenerationService takes key ID instead of PrivateKey (#4395) * refactor: TokenGenerationService takes key ID instead of PrivateKey * move JwsSignerProvider to its own SPI * Update spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/JwsSignerProvider.java Co-authored-by: Enrico Risa * Update core/common/token-core/src/main/java/org/eclipse/edc/token/JwtGenerationService.java Co-authored-by: ndr_brt * pr remarks * fix tests --------- Co-authored-by: Enrico Risa Co-authored-by: ndr_brt --- core/common/token-core/build.gradle.kts | 1 + .../edc/token/JwtGenerationService.java | 19 ++++++++----- .../edc/token/TokenServicesExtension.java | 15 ++++++++++ .../edc/token/JwtGenerationServiceTest.java | 25 +++++++++++------ .../DataPlaneDefaultIamServicesExtension.java | 18 ++++-------- ...efaultDataPlaneAccessTokenServiceImpl.java | 11 ++++---- .../core/DcpDefaultServicesExtension.java | 12 +++----- .../identity-trust-sts-core/build.gradle.kts | 1 + .../defaults/StsDefaultServicesExtension.java | 9 +++--- .../StsClientTokenGeneratorServiceImpl.java | 5 ++-- ...StsClientTokenIssuanceIntegrationTest.java | 8 ++++-- ...tsClientTokenGeneratorServiceImplTest.java | 2 +- .../embedded/EmbeddedSecureTokenService.java | 19 ++++++------- ...ddedSecureTokenServiceIntegrationTest.java | 6 ++-- .../EmbeddedSecureTokenServiceTest.java | 15 +++++----- .../iam/oauth2/Oauth2ServiceExtension.java | 9 +++--- .../oauth2/identity/Oauth2ServiceImpl.java | 9 +++--- .../identity/Oauth2ServiceImplTest.java | 4 +-- .../TransferDataPlaneCoreExtension.java | 17 +++-------- .../ConsumerPullDataPlaneProxyResolver.java | 9 +++--- ...onsumerPullDataPlaneProxyResolverTest.java | 8 +++--- .../oauth2/DataPlaneHttpOauth2Extension.java | 6 ++-- .../Oauth2CredentialsRequestFactory.java | 20 ++++--------- .../Oauth2CredentialsRequestFactoryTest.java | 27 +++++++++--------- settings.gradle.kts | 1 + spi/common/jwt-signer-spi/build.gradle.kts | 26 +++++++++++++++++ .../edc/jwt/signer/spi/JwsSignerProvider.java | 28 +++++++++++++++++++ .../edc/jwt/signer/spi/package-info.java | 18 ++++++++++++ .../edc/token/spi/TokenGenerationService.java | 10 ++----- 29 files changed, 215 insertions(+), 143 deletions(-) create mode 100644 spi/common/jwt-signer-spi/build.gradle.kts create mode 100644 spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/JwsSignerProvider.java create mode 100644 spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/package-info.java diff --git a/core/common/token-core/build.gradle.kts b/core/common/token-core/build.gradle.kts index 4ba70bb4da1..1a64db3b7af 100644 --- a/core/common/token-core/build.gradle.kts +++ b/core/common/token-core/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { api(project(":spi:common:keys-spi")) api(project(":spi:common:token-spi")) api(project(":spi:common:jwt-spi")) + api(project(":spi:common:jwt-signer-spi")) implementation(project(":core:common:lib:crypto-common-lib")) // for the CryptoConverter implementation(libs.nimbus.jwt) diff --git a/core/common/token-core/src/main/java/org/eclipse/edc/token/JwtGenerationService.java b/core/common/token-core/src/main/java/org/eclipse/edc/token/JwtGenerationService.java index d24b88e029d..31b61abf4fa 100644 --- a/core/common/token-core/src/main/java/org/eclipse/edc/token/JwtGenerationService.java +++ b/core/common/token-core/src/main/java/org/eclipse/edc/token/JwtGenerationService.java @@ -20,6 +20,7 @@ import com.nimbusds.jose.JWSHeader; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.security.token.jwt.CryptoConverter; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.iam.TokenParameters; @@ -29,25 +30,29 @@ import org.eclipse.edc.token.spi.TokenGenerationService; import org.jetbrains.annotations.NotNull; -import java.security.PrivateKey; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; -import java.util.function.Supplier; public class JwtGenerationService implements TokenGenerationService { + private final JwsSignerProvider jwsGeneratorFunction; + + public JwtGenerationService(JwsSignerProvider jwsSignerProvider) { + + this.jwsGeneratorFunction = jwsSignerProvider; + } @Override - public Result generate(Supplier privateKeySupplier, @NotNull TokenDecorator... decorators) { + public Result generate(String privateKeyId, @NotNull TokenDecorator... decorators) { - var privateKey = privateKeySupplier.get(); - if (privateKey == null) { - return Result.failure("PrivateKey cannot be resolved."); + var tokenSignerResult = jwsGeneratorFunction.createJwsSigner(privateKeyId); + if (tokenSignerResult.failed()) { + return Result.failure("JWSSigner cannot be generated for private key '%s': %s".formatted(privateKeyId, tokenSignerResult.getFailureDetail())); } - var tokenSigner = CryptoConverter.createSignerFor(privateKey); + var tokenSigner = tokenSignerResult.getContent(); var jwsAlgorithm = CryptoConverter.getRecommendedAlgorithm(tokenSigner); var bldr = TokenParameters.Builder.newInstance(); diff --git a/core/common/token-core/src/main/java/org/eclipse/edc/token/TokenServicesExtension.java b/core/common/token-core/src/main/java/org/eclipse/edc/token/TokenServicesExtension.java index f420f5146c8..3941cac5143 100644 --- a/core/common/token-core/src/main/java/org/eclipse/edc/token/TokenServicesExtension.java +++ b/core/common/token-core/src/main/java/org/eclipse/edc/token/TokenServicesExtension.java @@ -14,8 +14,13 @@ package org.eclipse.edc.token; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; +import org.eclipse.edc.keys.spi.PrivateKeyResolver; import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; +import org.eclipse.edc.security.token.jwt.CryptoConverter; +import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.token.spi.TokenDecoratorRegistry; import org.eclipse.edc.token.spi.TokenValidationRulesRegistry; @@ -31,6 +36,9 @@ public class TokenServicesExtension implements ServiceExtension { public static final String NAME = "Token Services Extension"; + @Inject + private PrivateKeyResolver privateKeyResolver; + @Provider public TokenValidationRulesRegistry tokenValidationRulesRegistry() { return new TokenValidationRulesRegistryImpl(); @@ -45,4 +53,11 @@ public TokenValidationService validationService() { public TokenDecoratorRegistry tokenDecoratorRegistry() { return new TokenDecoratorRegistryImpl(); } + + @Provider(isDefault = true) + public JwsSignerProvider defaultSignerProvider() { + // default implementation: resolve the private key (from vault of config) and create a JWSSigner based on its algorithm + return privateKeyId -> privateKeyResolver.resolvePrivateKey(privateKeyId) + .compose(pk -> Result.ofThrowable(() -> CryptoConverter.createSignerFor(pk))); + } } diff --git a/core/common/token-core/src/test/java/org/eclipse/edc/token/JwtGenerationServiceTest.java b/core/common/token-core/src/test/java/org/eclipse/edc/token/JwtGenerationServiceTest.java index 925f057c05a..044dda94195 100644 --- a/core/common/token-core/src/test/java/org/eclipse/edc/token/JwtGenerationServiceTest.java +++ b/core/common/token-core/src/test/java/org/eclipse/edc/token/JwtGenerationServiceTest.java @@ -23,6 +23,8 @@ import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; import com.nimbusds.jose.util.Base64URL; import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.security.token.jwt.CryptoConverter; +import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.token.spi.TokenDecorator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,26 +41,31 @@ class JwtGenerationServiceTest { + public static final String TEST_KEY_ID = "test-key-id"; private RSAKey keys; private JwtGenerationService tokenGenerationService; @BeforeEach void setUp() throws JOSEException { keys = testKey(); - tokenGenerationService = new JwtGenerationService(); + tokenGenerationService = new JwtGenerationService(keyId -> { + if (TEST_KEY_ID.equals(keyId)) { + try { + var pk = keys.toPrivateKey(); + return Result.success(CryptoConverter.createSignerFor(pk)); + } catch (JOSEException e) { + return Result.failure(e.getMessage()); + } + } + return Result.failure("key not found"); + }); } @Test void verifyTokenGeneration() throws ParseException, JOSEException { var decorator = testDecorator(); - var result = tokenGenerationService.generate(() -> { - try { - return keys.toPrivateKey(); - } catch (JOSEException e) { - throw new RuntimeException(e); - } - }, decorator); + var result = tokenGenerationService.generate(TEST_KEY_ID, decorator); assertThat(result.succeeded()).isTrue(); var token = result.getContent().getToken(); @@ -84,7 +91,7 @@ void verifyTokenGeneration() throws ParseException, JOSEException { void shouldFail_whenPrivateKeyCannotBeResolved() { var decorator = testDecorator(); - var result = tokenGenerationService.generate(() -> null, decorator); + var result = tokenGenerationService.generate("not-exist-key", decorator); assertThat(result.failed()).isTrue(); } diff --git a/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/DataPlaneDefaultIamServicesExtension.java b/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/DataPlaneDefaultIamServicesExtension.java index c0d59fec35d..21bb5ec6179 100644 --- a/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/DataPlaneDefaultIamServicesExtension.java +++ b/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/DataPlaneDefaultIamServicesExtension.java @@ -18,21 +18,19 @@ import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessControlService; import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessTokenService; import org.eclipse.edc.connector.dataplane.spi.store.AccessTokenDataStore; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.keys.spi.LocalPublicKeyService; import org.eclipse.edc.keys.spi.PrivateKeyResolver; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.token.JwtGenerationService; import org.eclipse.edc.token.spi.TokenValidationService; -import org.jetbrains.annotations.NotNull; -import java.security.PrivateKey; import java.util.function.Supplier; @@ -54,6 +52,8 @@ public class DataPlaneDefaultIamServicesExtension implements ServiceExtension { private PrivateKeyResolver privateKeyResolver; @Inject private LocalPublicKeyService localPublicKeyService; + @Inject + private JwsSignerProvider jwsSignerProvider; @Override public String name() { @@ -71,8 +71,8 @@ public DataPlaneAccessTokenService defaultAccessTokenService(ServiceExtensionCon var tokenVerifierPublicKeyAlias = context.getConfig().getString(TOKEN_VERIFIER_PUBLIC_KEY_ALIAS); var tokenSignerPrivateKeyAlias = context.getConfig().getString(TOKEN_SIGNER_PRIVATE_KEY_ALIAS); var monitor = context.getMonitor().withPrefix("DataPlane IAM"); - return new DefaultDataPlaneAccessTokenServiceImpl(new JwtGenerationService(), - accessTokenDataStore, monitor, getPrivateKeySupplier(tokenSignerPrivateKeyAlias, monitor), + return new DefaultDataPlaneAccessTokenServiceImpl(new JwtGenerationService(jwsSignerProvider), + accessTokenDataStore, monitor, () -> tokenSignerPrivateKeyAlias, publicKeyIdSupplier(tokenVerifierPublicKeyAlias), tokenValidationService, localPublicKeyService); } @@ -80,12 +80,4 @@ private Supplier publicKeyIdSupplier(String tokenVerifierPublicKeyAlias) return () -> tokenVerifierPublicKeyAlias; } - @NotNull - private Supplier getPrivateKeySupplier(String tokenSignerPrivateKeyAlias, Monitor monitor) { - return () -> privateKeyResolver.resolvePrivateKey(tokenSignerPrivateKeyAlias) - .orElse(f -> { - monitor.warning("Cannot resolve private key: " + f.getFailureDetail()); - return null; - }); - } } diff --git a/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/iam/DefaultDataPlaneAccessTokenServiceImpl.java b/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/iam/DefaultDataPlaneAccessTokenServiceImpl.java index c9017c9cbb7..5cfd46c0071 100644 --- a/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/iam/DefaultDataPlaneAccessTokenServiceImpl.java +++ b/core/data-plane/data-plane-core/src/main/java/org/eclipse/edc/connector/dataplane/framework/iam/DefaultDataPlaneAccessTokenServiceImpl.java @@ -32,7 +32,6 @@ import org.eclipse.edc.token.spi.TokenValidationRule; import org.eclipse.edc.token.spi.TokenValidationService; -import java.security.PrivateKey; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -56,7 +55,7 @@ public class DefaultDataPlaneAccessTokenServiceImpl implements DataPlaneAccessTo private final TokenGenerationService tokenGenerationService; private final AccessTokenDataStore accessTokenDataStore; private final Monitor monitor; - private final Supplier privateKeySupplier; + private final Supplier privateKeyIdSupplier; private final Supplier publicKeyIdSupplier; private final TokenValidationService tokenValidationService; private final PublicKeyResolver publicKeyResolver; @@ -64,14 +63,14 @@ public class DefaultDataPlaneAccessTokenServiceImpl implements DataPlaneAccessTo public DefaultDataPlaneAccessTokenServiceImpl(TokenGenerationService tokenGenerationService, AccessTokenDataStore accessTokenDataStore, Monitor monitor, - Supplier privateKeySupplier, + Supplier privateKeyIdSupplier, Supplier publicKeyIdSupplier, TokenValidationService tokenValidationService, PublicKeyResolver publicKeyResolver) { this.tokenGenerationService = tokenGenerationService; this.accessTokenDataStore = accessTokenDataStore; this.monitor = monitor; - this.privateKeySupplier = privateKeySupplier; + this.privateKeyIdSupplier = privateKeyIdSupplier; this.publicKeyIdSupplier = publicKeyIdSupplier; this.tokenValidationService = tokenValidationService; this.publicKeyResolver = publicKeyResolver; @@ -106,9 +105,9 @@ public Result obtainToken(TokenParameters parameters, DataA allDecorators.add(tokenIdDecorator); } - var tokenResult = tokenGenerationService.generate(privateKeySupplier, allDecorators.toArray(new TokenDecorator[0])); + var tokenResult = tokenGenerationService.generate(privateKeyIdSupplier.get(), allDecorators.toArray(new TokenDecorator[0])); if (tokenResult.failed()) { - return tokenResult.mapTo(); + return tokenResult.mapEmpty(); } // store a record of the token for future reference. We'll need that when we resolve the AccessTokenData later. diff --git a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/DcpDefaultServicesExtension.java b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/DcpDefaultServicesExtension.java index 76c9bd34d9a..212285d3b63 100644 --- a/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/DcpDefaultServicesExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-core/src/main/java/org/eclipse/edc/iam/identitytrust/core/DcpDefaultServicesExtension.java @@ -25,12 +25,11 @@ import org.eclipse.edc.iam.identitytrust.spi.verification.SignatureSuiteRegistry; import org.eclipse.edc.iam.identitytrust.sts.embedded.EmbeddedSecureTokenService; import org.eclipse.edc.iam.verifiablecredentials.spi.validation.TrustedIssuerRegistry; -import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.runtime.metamodel.annotation.Setting; -import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.iam.AudienceResolver; import org.eclipse.edc.spi.iam.ClaimToken; import org.eclipse.edc.spi.result.Result; @@ -38,11 +37,9 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.token.JwtGenerationService; -import java.security.PrivateKey; import java.time.Clock; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import static org.eclipse.edc.spi.result.Result.failure; import static org.eclipse.edc.spi.result.Result.success; @@ -63,7 +60,7 @@ public class DcpDefaultServicesExtension implements ServiceExtension { @Inject private Clock clock; @Inject - private PrivateKeyResolver privateKeyResolver; + private JwsSignerProvider externalSigner; @Provider(isDefault = true) public SecureTokenService createDefaultTokenService(ServiceExtensionContext context) { @@ -78,10 +75,9 @@ public SecureTokenService createDefaultTokenService(ServiceExtensionContext cont var publicKeyId = context.getSetting(STS_PUBLIC_KEY_ID, null); - var privKeyAlias = context.getSetting(STS_PRIVATE_KEY_ALIAS, null); + var privateKeyAlias = context.getSetting(STS_PRIVATE_KEY_ALIAS, null); - Supplier supplier = () -> privateKeyResolver.resolvePrivateKey(privKeyAlias).orElseThrow(f -> new EdcException("This EDC instance is not operational due to the following error: %s".formatted(f.getFailureDetail()))); - return new EmbeddedSecureTokenService(new JwtGenerationService(), supplier, () -> publicKeyId, clock, TimeUnit.MINUTES.toSeconds(tokenExpiration)); + return new EmbeddedSecureTokenService(new JwtGenerationService(externalSigner), () -> privateKeyAlias, () -> publicKeyId, clock, TimeUnit.MINUTES.toSeconds(tokenExpiration)); } @Provider(isDefault = true) diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/build.gradle.kts b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/build.gradle.kts index 6746595d4ad..341e388b8b0 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/build.gradle.kts +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { testImplementation(testFixtures(project(":spi:common:identity-trust-sts-spi"))) testImplementation(project(":core:common:lib:boot-lib")) + testImplementation(project(":core:common:lib:crypto-common-lib")) testImplementation(project(":core:common:lib:keys-lib")) testImplementation(project(":core:common:junit")) testImplementation(libs.nimbus.jwt) diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java index 88dc54ad83d..5ce08a9e8f3 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/defaults/StsDefaultServicesExtension.java @@ -16,10 +16,11 @@ import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientServiceImpl; import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientTokenGeneratorServiceImpl; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientService; import org.eclipse.edc.iam.identitytrust.sts.spi.service.StsClientTokenGeneratorService; import org.eclipse.edc.iam.identitytrust.sts.spi.store.StsClientStore; -import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.runtime.metamodel.annotation.Provider; @@ -53,7 +54,7 @@ public class StsDefaultServicesExtension implements ServiceExtension { private Vault vault; @Inject - private PrivateKeyResolver privateKeyResolver; + private JwsSignerProvider jwsSignerProvider; @Inject private Clock clock; @@ -67,8 +68,8 @@ public String name() { public StsClientTokenGeneratorService clientTokenService(ServiceExtensionContext context) { var tokenExpiration = context.getSetting(STS_TOKEN_EXPIRATION, DEFAULT_STS_TOKEN_EXPIRATION_MIN); return new StsClientTokenGeneratorServiceImpl( - (client) -> new JwtGenerationService(), - (client) -> privateKeyResolver.resolvePrivateKey(client.getPrivateKeyAlias()).orElse(null), + (client) -> new JwtGenerationService(jwsSignerProvider), + StsClient::getPrivateKeyAlias, clock, TimeUnit.MINUTES.toSeconds(tokenExpiration)); } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/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/defaults/service/StsClientTokenGeneratorServiceImpl.java index 8f585580378..75ce0c58e2f 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/main/java/org/eclipse/edc/iam/identitytrust/sts/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/defaults/service/StsClientTokenGeneratorServiceImpl.java @@ -22,7 +22,6 @@ import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.ServiceResult; -import java.security.PrivateKey; import java.time.Clock; import java.util.Collections; import java.util.HashMap; @@ -42,10 +41,10 @@ public class StsClientTokenGeneratorServiceImpl implements StsClientTokenGenerat private final long tokenExpiration; private final StsTokenGenerationProvider tokenGenerationProvider; - private final Function keyFunction; + private final Function keyFunction; private final Clock clock; - public StsClientTokenGeneratorServiceImpl(StsTokenGenerationProvider tokenGenerationProvider, Function keyFunction, Clock clock, long tokenExpiration) { + public StsClientTokenGeneratorServiceImpl(StsTokenGenerationProvider tokenGenerationProvider, Function keyFunction, Clock clock, long tokenExpiration) { this.tokenGenerationProvider = tokenGenerationProvider; this.keyFunction = keyFunction; this.clock = clock; diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/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/defaults/StsClientTokenIssuanceIntegrationTest.java index 76513774da8..b06b8ac5b1b 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/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/defaults/StsClientTokenIssuanceIntegrationTest.java @@ -20,6 +20,7 @@ import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientServiceImpl; import org.eclipse.edc.iam.identitytrust.sts.defaults.service.StsClientTokenGeneratorServiceImpl; import org.eclipse.edc.iam.identitytrust.sts.defaults.store.InMemoryStsClientStore; +import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClient; import org.eclipse.edc.iam.identitytrust.sts.spi.model.StsClientTokenAdditionalParams; import org.eclipse.edc.junit.annotations.ComponentTest; import org.eclipse.edc.keys.KeyParserRegistryImpl; @@ -28,6 +29,8 @@ import org.eclipse.edc.keys.keyparsers.PemParser; import org.eclipse.edc.keys.spi.KeyParserRegistry; import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.security.token.jwt.CryptoConverter; +import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.token.JwtGenerationService; import org.eclipse.edc.transaction.spi.NoopTransactionContext; @@ -70,8 +73,9 @@ void setup() { privateKeyResolver = new VaultPrivateKeyResolver(keyParserRegistry, vault, mock(), mock()); tokenGeneratorService = new StsClientTokenGeneratorServiceImpl( - client -> new JwtGenerationService(), - stsClient -> privateKeyResolver.resolvePrivateKey(stsClient.getPrivateKeyAlias()).orElse(null), + client -> new JwtGenerationService(keyId -> privateKeyResolver.resolvePrivateKey(keyId) + .compose(pk -> Result.ofThrowable(() -> CryptoConverter.createSignerFor(pk)))), + StsClient::getPrivateKeyAlias, Clock.systemUTC(), 60 * 5); } diff --git a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java index b6e97b676dd..23dc0a86c44 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-core/src/test/java/org/eclipse/edc/iam/identitytrust/sts/defaults/service/StsClientTokenGeneratorServiceImplTest.java @@ -43,7 +43,7 @@ public class StsClientTokenGeneratorServiceImplTest { @BeforeEach void setup() { - clientTokenService = new StsClientTokenGeneratorServiceImpl(tokenGenerationProvider, (client) -> mock(), Clock.systemUTC(), TOKEN_EXPIRATION); + clientTokenService = new StsClientTokenGeneratorServiceImpl(tokenGenerationProvider, (client) -> "test-key-id", Clock.systemUTC(), TOKEN_EXPIRATION); } @Test 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 4af49ad7489..de2b4386666 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 @@ -21,7 +21,6 @@ import org.eclipse.edc.token.spi.TokenGenerationService; import org.jetbrains.annotations.Nullable; -import java.security.PrivateKey; import java.time.Clock; import java.util.HashMap; import java.util.List; @@ -50,15 +49,15 @@ public class EmbeddedSecureTokenService implements SecureTokenService { private static final List ACCESS_TOKEN_INHERITED_CLAIMS = List.of(ISSUER); private final TokenGenerationService tokenGenerationService; - private final Supplier privateKeySupplier; - private final Supplier publicKeyId; + private final Supplier privateKeyIdSupplier; + private final Supplier publicKeyIdSupplier; private final Clock clock; private final long validity; - public EmbeddedSecureTokenService(TokenGenerationService tokenGenerationService, Supplier privateKeySupplier, Supplier publicKeyId, Clock clock, long validity) { + public EmbeddedSecureTokenService(TokenGenerationService tokenGenerationService, Supplier privateKeyIdSupplier, Supplier publicKeyIdSupplier, Clock clock, long validity) { this.tokenGenerationService = tokenGenerationService; - this.privateKeySupplier = privateKeySupplier; - this.publicKeyId = publicKeyId; + this.privateKeyIdSupplier = privateKeyIdSupplier; + this.publicKeyIdSupplier = publicKeyIdSupplier; this.clock = clock; this.validity = validity; } @@ -70,8 +69,8 @@ public Result createToken(Map claims, @Null .map(scope -> createAndAcceptAccessToken(claims, scope, selfIssuedClaims::put)) .orElse(success()) .compose(v -> { - var keyIdDecorator = new KeyIdDecorator(publicKeyId.get()); - return tokenGenerationService.generate(privateKeySupplier, keyIdDecorator, new SelfIssuedTokenDecorator(selfIssuedClaims, clock, validity)); + var keyIdDecorator = new KeyIdDecorator(publicKeyIdSupplier.get()); + return tokenGenerationService.generate(privateKeyIdSupplier.get(), keyIdDecorator, new SelfIssuedTokenDecorator(selfIssuedClaims, clock, validity)); }); } @@ -88,8 +87,8 @@ private Result createAccessToken(Map claims return addClaim(claims, ISSUER, withClaim(AUDIENCE, accessTokenClaims::put)) .compose(v -> addClaim(claims, AUDIENCE, withClaim(SUBJECT, accessTokenClaims::put))) .compose(v -> { - var keyIdDecorator = new KeyIdDecorator(publicKeyId.get()); - return tokenGenerationService.generate(privateKeySupplier, keyIdDecorator, new SelfIssuedTokenDecorator(accessTokenClaims, clock, validity)); + var keyIdDecorator = new KeyIdDecorator(publicKeyIdSupplier.get()); + return tokenGenerationService.generate(privateKeyIdSupplier.get(), keyIdDecorator, 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 cd18011feab..58758c7910d 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 @@ -17,8 +17,10 @@ import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; import com.nimbusds.jwt.SignedJWT; +import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.token.JwtGenerationService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -63,8 +65,8 @@ private static KeyPair generateKeyPair() throws NoSuchAlgorithmException { @BeforeEach void setup() throws NoSuchAlgorithmException { keyPair = generateKeyPair(); - var tokenGenerationService = new JwtGenerationService(); - secureTokenService = new EmbeddedSecureTokenService(tokenGenerationService, () -> keyPair.getPrivate(), () -> "test-keyid", Clock.systemUTC(), 10 * 60); + var tokenGenerationService = new JwtGenerationService(s -> Result.success(new RSASSASigner(keyPair.getPrivate()))); + secureTokenService = new EmbeddedSecureTokenService(tokenGenerationService, () -> "test-private-keyid", () -> "test-keyid", Clock.systemUTC(), 10 * 60); } @Test 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/EmbeddedSecureTokenServiceTest.java b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceTest.java index e8a958a639b..09e7ef4ad74 100644 --- a/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceTest.java +++ b/extensions/common/iam/identity-trust/identity-trust-sts/identity-trust-sts-embedded/src/test/java/org/eclipse/edc/iam/identitytrust/sts/embedded/EmbeddedSecureTokenServiceTest.java @@ -21,9 +21,7 @@ import org.eclipse.edc.token.spi.TokenGenerationService; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; -import java.security.PrivateKey; import java.time.Clock; import java.util.Map; import java.util.function.Supplier; @@ -40,15 +38,16 @@ public class EmbeddedSecureTokenServiceTest { + public static final String TEST_PRIVATEKEY_ID = "test-privatekey-id"; private final TokenGenerationService tokenGenerationService = mock(); - private final Supplier keySupplier = Mockito::mock; + private final Supplier keySupplier = () -> TEST_PRIVATEKEY_ID; @Test void createToken_withoutBearerAccessScope() { var sts = new EmbeddedSecureTokenService(tokenGenerationService, keySupplier, () -> "test-key", Clock.systemUTC(), 10 * 60); var token = TokenRepresentation.Builder.newInstance().token("test").build(); - when(tokenGenerationService.generate(eq(keySupplier), any(TokenDecorator[].class))).thenReturn(Result.success(token)); + when(tokenGenerationService.generate(eq(TEST_PRIVATEKEY_ID), any(TokenDecorator[].class))).thenReturn(Result.success(token)); var result = sts.createToken(Map.of(), null); assertThat(result.succeeded()).isTrue(); @@ -71,7 +70,7 @@ void createToken_withBearerAccessScope() { var sts = new EmbeddedSecureTokenService(tokenGenerationService, keySupplier, () -> "test-key", Clock.systemUTC(), 10 * 60); var token = TokenRepresentation.Builder.newInstance().token("test").build(); - when(tokenGenerationService.generate(eq(keySupplier), any(TokenDecorator[].class))) + when(tokenGenerationService.generate(eq(TEST_PRIVATEKEY_ID), any(TokenDecorator[].class))) .thenReturn(Result.success(token)) .thenReturn(Result.success(token)); @@ -104,7 +103,7 @@ void createToken_error_whenAccessTokenFails() { var sts = new EmbeddedSecureTokenService(tokenGenerationService, keySupplier, () -> "test-key", Clock.systemUTC(), 10 * 60); var token = TokenRepresentation.Builder.newInstance().token("test").build(); - when(tokenGenerationService.generate(eq(keySupplier), any(TokenDecorator[].class))) + when(tokenGenerationService.generate(eq(TEST_PRIVATEKEY_ID), any(TokenDecorator[].class))) .thenReturn(Result.failure("Failed to create access token")) .thenReturn(Result.success(token)); @@ -128,7 +127,7 @@ void createToken_error_whenSelfTokenFails() { var sts = new EmbeddedSecureTokenService(tokenGenerationService, keySupplier, () -> "test-key", Clock.systemUTC(), 10 * 60); var token = TokenRepresentation.Builder.newInstance().token("test").build(); - when(tokenGenerationService.generate(eq(keySupplier), any(TokenDecorator[].class))) + when(tokenGenerationService.generate(eq(TEST_PRIVATEKEY_ID), any(TokenDecorator[].class))) .thenReturn(Result.success(token)) .thenReturn(Result.failure("Failed to create access token")); @@ -137,7 +136,7 @@ void createToken_error_whenSelfTokenFails() { assertThat(result.failed()).isTrue(); - verify(tokenGenerationService, times(2)).generate(eq(keySupplier), any(TokenDecorator[].class)); + verify(tokenGenerationService, times(2)).generate(eq(TEST_PRIVATEKEY_ID), any(TokenDecorator[].class)); } diff --git a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java index b1743856bb8..b9959779af7 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java @@ -22,6 +22,7 @@ import org.eclipse.edc.iam.oauth2.jwt.X509CertificateDecorator; import org.eclipse.edc.iam.oauth2.spi.Oauth2AssertionDecorator; import org.eclipse.edc.iam.oauth2.spi.client.Oauth2Client; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.keys.spi.CertificateResolver; import org.eclipse.edc.keys.spi.PrivateKeyResolver; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -42,7 +43,6 @@ import org.eclipse.edc.token.spi.TokenValidationService; import org.jetbrains.annotations.NotNull; -import java.security.PrivateKey; import java.time.Clock; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -110,6 +110,8 @@ public class Oauth2ServiceExtension implements ServiceExtension { private TokenValidationService tokenValidationService; @Inject private TokenDecoratorRegistry jwtDecoratorRegistry; + @Inject + private JwsSignerProvider jwsSignerProvider; @Override public String name() { @@ -157,12 +159,11 @@ private IdentityProviderKeyResolver identityProviderKeyResolver(ServiceExtension private Oauth2ServiceImpl createOauth2Service(Oauth2ServiceConfiguration configuration, TokenDecoratorRegistry jwtDecoratorRegistry, IdentityProviderKeyResolver providerKeyResolver) { - Supplier privateKeySupplier = () -> privateKeyResolver.resolvePrivateKey(configuration.getPrivateKeyAlias()) - .orElseThrow(f -> new EdcException(f.getFailureDetail())); + Supplier privateKeySupplier = configuration::getPrivateKeyAlias; return new Oauth2ServiceImpl( configuration, - new JwtGenerationService(), + new JwtGenerationService(jwsSignerProvider), privateKeySupplier, oauth2Client, jwtDecoratorRegistry, diff --git a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImpl.java b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImpl.java index f03dc8eb468..7300bb8ce82 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImpl.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImpl.java @@ -36,7 +36,6 @@ import org.eclipse.edc.token.spi.TokenValidationService; import org.jetbrains.annotations.NotNull; -import java.security.PrivateKey; import java.util.function.Supplier; import static org.eclipse.edc.iam.oauth2.Oauth2ServiceExtension.OAUTH2_TOKEN_CONTEXT; @@ -49,7 +48,7 @@ public class Oauth2ServiceImpl implements IdentityService { private static final String GRANT_TYPE = "client_credentials"; private final Oauth2ServiceConfiguration configuration; - private final Supplier privateKeySupplier; + private final Supplier privateKeySupplier; private final Oauth2Client client; private final TokenDecoratorRegistry jwtDecoratorRegistry; private final TokenGenerationService tokenGenerationService; @@ -66,11 +65,11 @@ public class Oauth2ServiceImpl implements IdentityService { * @param jwtDecoratorRegistry Registry containing the decorator for build the JWT * @param tokenValidationService Service used for token validation */ - public Oauth2ServiceImpl(Oauth2ServiceConfiguration configuration, TokenGenerationService tokenGenerationService, Supplier privateKeySupplier, + public Oauth2ServiceImpl(Oauth2ServiceConfiguration configuration, TokenGenerationService tokenGenerationService, Supplier privateKeyIdSupplier, Oauth2Client client, TokenDecoratorRegistry jwtDecoratorRegistry, TokenValidationRulesRegistry tokenValidationRuleRegistry, TokenValidationService tokenValidationService, PublicKeyResolver publicKeyResolver) { this.configuration = configuration; - this.privateKeySupplier = privateKeySupplier; + this.privateKeySupplier = privateKeyIdSupplier; this.client = client; this.jwtDecoratorRegistry = jwtDecoratorRegistry; this.tokenValidationRuleRegistry = tokenValidationRuleRegistry; @@ -94,7 +93,7 @@ public Result verifyJwtToken(TokenRepresentation tokenRepresentation @NotNull private Result generateClientAssertion() { var decorators = jwtDecoratorRegistry.getDecoratorsFor(OAUTH2_TOKEN_CONTEXT).toArray(TokenDecorator[]::new); - return tokenGenerationService.generate(privateKeySupplier, decorators) + return tokenGenerationService.generate(privateKeySupplier.get(), decorators) .map(TokenRepresentation::getToken); } diff --git a/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java b/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java index a8aa3d22129..05988d9cc57 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/test/java/org/eclipse/edc/iam/oauth2/identity/Oauth2ServiceImplTest.java @@ -75,6 +75,7 @@ class Oauth2ServiceImplTest { private static final String PROVIDER_AUDIENCE = "audience-test"; private static final String ENDPOINT_AUDIENCE = "endpoint-audience-test"; + private static final String TEST_PRIVATE_KEY_ID = "test-private-key-id"; private static final VerificationContext VERIFICATION_CONTEXT = VerificationContext.Builder.newInstance() .policy(Policy.Builder.newInstance().build()) .build(); @@ -90,7 +91,6 @@ class Oauth2ServiceImplTest { @BeforeEach void setUp() throws JOSEException { var testKey = testKey(); - var privateKey = testKey.toPrivateKey(); jwsSigner = new RSASSASigner(testKey.toPrivateKey()); var publicKeyResolverMock = mock(PublicKeyResolver.class); @@ -114,7 +114,7 @@ void setUp() throws JOSEException { registry.addRule(OAUTH2_TOKEN_CONTEXT, new NotBeforeValidationRule(Clock.systemUTC(), configuration.getNotBeforeValidationLeeway())); registry.addRule(OAUTH2_TOKEN_CONTEXT, new ExpirationIssuedAtValidationRule(Clock.systemUTC(), configuration.getIssuedAtLeeway())); - authService = new Oauth2ServiceImpl(configuration, tokenGenerationService, () -> privateKey, client, jwtDecoratorRegistry, registry, + authService = new Oauth2ServiceImpl(configuration, tokenGenerationService, () -> TEST_PRIVATE_KEY_ID, client, jwtDecoratorRegistry, registry, tokenValidationService, publicKeyResolverMock); } diff --git a/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/TransferDataPlaneCoreExtension.java b/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/TransferDataPlaneCoreExtension.java index 056d4942628..acb5a1abfa0 100644 --- a/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/TransferDataPlaneCoreExtension.java +++ b/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/TransferDataPlaneCoreExtension.java @@ -25,6 +25,7 @@ import org.eclipse.edc.connector.controlplane.transfer.spi.flow.DataFlowManager; import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; import org.eclipse.edc.connector.dataplane.selector.spi.client.DataPlaneClientFactory; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.keys.spi.LocalPublicKeyService; import org.eclipse.edc.keys.spi.PrivateKeyResolver; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -41,11 +42,8 @@ import org.eclipse.edc.web.spi.WebService; import org.eclipse.edc.web.spi.configuration.ApiContext; import org.eclipse.edc.web.spi.configuration.context.ControlApiUrl; -import org.jetbrains.annotations.NotNull; -import java.security.PrivateKey; import java.time.Clock; -import java.util.function.Supplier; @Extension(value = TransferDataPlaneCoreExtension.NAME) public class TransferDataPlaneCoreExtension implements ServiceExtension { @@ -100,6 +98,8 @@ public class TransferDataPlaneCoreExtension implements ServiceExtension { @Inject private TokenValidationService tokenValidationService; + @Inject + private JwsSignerProvider jwsSignerProvider; @Override public String name() { @@ -117,7 +117,7 @@ public void initialize(ServiceExtensionContext context) { var controller = new ConsumerPullTransferTokenValidationApiController(tokenValidationService, dataEncrypter, typeManager, (i) -> publicKeyService.resolveKey(publicKeyAlias)); webService.registerResource(ApiContext.CONTROL, controller); - var resolver = new ConsumerPullDataPlaneProxyResolver(dataEncrypter, typeManager, new JwtGenerationService(), getPrivateKeySupplier(context, privateKeyAlias), () -> publicKeyAlias, tokenExpirationDateFunction); + var resolver = new ConsumerPullDataPlaneProxyResolver(dataEncrypter, typeManager, new JwtGenerationService(jwsSignerProvider), () -> privateKeyAlias, () -> publicKeyAlias, tokenExpirationDateFunction); dataFlowManager.register(new ConsumerPullTransferDataFlowController(selectorService, resolver)); } else { monitor.info("One of these settings is not configured, so the connector won't be able to provide 'consumer-pull' transfers: [%s, %s]" @@ -130,13 +130,4 @@ public void initialize(ServiceExtensionContext context) { dataAddressValidatorRegistry.registerDestinationValidator("HttpProxy", dataAddress -> ValidationResult.success()); } - @NotNull - private Supplier getPrivateKeySupplier(ServiceExtensionContext context, String privateKeyAlias) { - return () -> privateKeyResolver.resolvePrivateKey(privateKeyAlias) - .orElse(f -> { - context.getMonitor().warning("Cannot resolve private key: " + f.getFailureDetail()); - return null; - }); - } - } diff --git a/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolver.java b/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolver.java index 1468e290615..be4ac830e5b 100644 --- a/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolver.java +++ b/extensions/control-plane/transfer/transfer-data-plane/src/main/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolver.java @@ -27,7 +27,6 @@ import org.eclipse.edc.token.spi.KeyIdDecorator; import org.eclipse.edc.token.spi.TokenGenerationService; -import java.security.PrivateKey; import java.util.Optional; import java.util.function.Supplier; @@ -42,18 +41,18 @@ public class ConsumerPullDataPlaneProxyResolver { private final DataEncrypter dataEncrypter; private final TypeManager typeManager; private final TokenGenerationService tokenGenerationService; - private final Supplier keySupplier; + private final Supplier privateKeyIdSupplier; private final Supplier publicKeyIdSupplier; private final ConsumerPullTokenExpirationDateFunction tokenExpirationDateFunction; public ConsumerPullDataPlaneProxyResolver(DataEncrypter dataEncrypter, TypeManager typeManager, TokenGenerationService tokenGenerationService, - Supplier keySupplier, Supplier publicKeyIdSupplier, + Supplier privateKeyIdSupplier, Supplier publicKeyIdSupplier, ConsumerPullTokenExpirationDateFunction tokenExpirationDateFunction) { this.dataEncrypter = dataEncrypter; this.typeManager = typeManager; this.tokenExpirationDateFunction = tokenExpirationDateFunction; this.tokenGenerationService = tokenGenerationService; - this.keySupplier = keySupplier; + this.privateKeyIdSupplier = privateKeyIdSupplier; this.publicKeyIdSupplier = publicKeyIdSupplier; } @@ -87,7 +86,7 @@ private Result generateAccessToken(DataAddress source, String contractId .compose(expiration -> { var keyIdDecorator = new KeyIdDecorator(publicKeyIdSupplier.get()); var dataAddressDecorator = new ConsumerPullDataPlaneProxyTokenDecorator(expiration, encryptedDataAddress); - return tokenGenerationService.generate(keySupplier, keyIdDecorator, dataAddressDecorator); + return tokenGenerationService.generate(privateKeyIdSupplier.get(), keyIdDecorator, dataAddressDecorator); }) .map(TokenRepresentation::getToken); } diff --git a/extensions/control-plane/transfer/transfer-data-plane/src/test/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolverTest.java b/extensions/control-plane/transfer/transfer-data-plane/src/test/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolverTest.java index fa8959761a8..6eca8795d82 100644 --- a/extensions/control-plane/transfer/transfer-data-plane/src/test/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolverTest.java +++ b/extensions/control-plane/transfer/transfer-data-plane/src/test/java/org/eclipse/edc/connector/controlplane/transfer/dataplane/proxy/ConsumerPullDataPlaneProxyResolverTest.java @@ -30,7 +30,6 @@ import org.eclipse.edc.token.spi.TokenGenerationService; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import java.sql.Date; import java.time.Instant; @@ -41,6 +40,7 @@ import static org.eclipse.edc.connector.controlplane.transfer.dataplane.spi.TransferDataPlaneConstants.DATA_ADDRESS; import static org.eclipse.edc.connector.controlplane.transfer.dataplane.spi.TransferDataPlaneConstants.HTTP_PROXY; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -52,7 +52,7 @@ class ConsumerPullDataPlaneProxyResolverTest { private final TokenGenerationService tokenGenerationService = mock(); private final ConsumerPullTokenExpirationDateFunction tokenExpirationDateFunction = mock(); - private final ConsumerPullDataPlaneProxyResolver resolver = new ConsumerPullDataPlaneProxyResolver(dataEncrypter, TYPE_MANAGER, tokenGenerationService, Mockito::mock, () -> "test-public-key", tokenExpirationDateFunction); + private final ConsumerPullDataPlaneProxyResolver resolver = new ConsumerPullDataPlaneProxyResolver(dataEncrypter, TYPE_MANAGER, tokenGenerationService, () -> "test-private-key", () -> "test-public-key", tokenExpirationDateFunction); private static DataAddress dataAddress() { return DataAddress.Builder.newInstance().type(UUID.randomUUID().toString()).build(); @@ -75,7 +75,7 @@ void verifyToDataAddressSuccess() { var captor = ArgumentCaptor.forClass(TokenDecorator[].class); when(dataEncrypter.encrypt(TYPE_MANAGER.writeValueAsString(address))).thenReturn(encryptedAddress); when(tokenExpirationDateFunction.expiresAt(address, transferProcess.getContractId())).thenReturn(Result.success(expiration)); - when(tokenGenerationService.generate(any(), captor.capture())) + when(tokenGenerationService.generate(anyString(), captor.capture())) .thenReturn(Result.success(TokenRepresentation.Builder.newInstance().token(token).build())); var result = resolver.toDataAddress(transferProcess, address, instance); @@ -157,7 +157,7 @@ void verifyToDataAddressReturnsFailureIfTokenGenerationFails() { when(dataEncrypter.encrypt(TYPE_MANAGER.writeValueAsString(address))).thenReturn("encryptedAddress"); when(tokenExpirationDateFunction.expiresAt(address, transferProcess.getContractId())).thenReturn(Result.success(expiration)); - when(tokenGenerationService.generate(any(), any(TokenDecorator[].class))).thenReturn(Result.failure(errorMsg)); + when(tokenGenerationService.generate(anyString(), any(TokenDecorator[].class))).thenReturn(Result.failure(errorMsg)); var result = resolver.toDataAddress(transferProcess, address, instance); diff --git a/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/DataPlaneHttpOauth2Extension.java b/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/DataPlaneHttpOauth2Extension.java index d14fe40065e..54f2638d2b6 100644 --- a/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/DataPlaneHttpOauth2Extension.java +++ b/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/DataPlaneHttpOauth2Extension.java @@ -17,7 +17,7 @@ import org.eclipse.edc.connector.dataplane.http.spi.HttpRequestParamsProvider; import org.eclipse.edc.iam.oauth2.spi.client.Oauth2Client; -import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.security.Vault; @@ -43,7 +43,7 @@ public class DataPlaneHttpOauth2Extension implements ServiceExtension { private Vault vault; @Inject - private PrivateKeyResolver privateKeyResolver; + private JwsSignerProvider jwsSignerProvider; @Inject private Oauth2Client oauth2Client; @@ -55,7 +55,7 @@ public String name() { @Override public void initialize(ServiceExtensionContext context) { - var requestFactory = new Oauth2CredentialsRequestFactory(privateKeyResolver, clock, vault, context.getMonitor()); + var requestFactory = new Oauth2CredentialsRequestFactory(jwsSignerProvider, clock, vault); var oauth2ParamsDecorator = new Oauth2HttpRequestParamsDecorator(requestFactory, oauth2Client); paramsProvider.registerSourceDecorator(oauth2ParamsDecorator); diff --git a/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactory.java b/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactory.java index 7520a9e5fca..12ffd294645 100644 --- a/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactory.java +++ b/extensions/data-plane/data-plane-http-oauth2-core/src/main/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactory.java @@ -18,9 +18,8 @@ import org.eclipse.edc.iam.oauth2.spi.client.Oauth2CredentialsRequest; import org.eclipse.edc.iam.oauth2.spi.client.PrivateKeyOauth2CredentialsRequest; import org.eclipse.edc.iam.oauth2.spi.client.SharedSecretOauth2CredentialsRequest; -import org.eclipse.edc.keys.spi.PrivateKeyResolver; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.spi.iam.TokenRepresentation; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.security.Vault; import org.eclipse.edc.spi.types.domain.DataAddress; @@ -46,16 +45,14 @@ public class Oauth2CredentialsRequestFactory { private static final long DEFAULT_TOKEN_VALIDITY = TimeUnit.MINUTES.toSeconds(5); private static final String GRANT_CLIENT_CREDENTIALS = "client_credentials"; - private final PrivateKeyResolver privateKeyResolver; + private final JwsSignerProvider jwsSignerProvider; private final Clock clock; private final Vault vault; - private final Monitor monitor; - public Oauth2CredentialsRequestFactory(PrivateKeyResolver privateKeyResolver, Clock clock, Vault vault, Monitor monitor) { - this.privateKeyResolver = privateKeyResolver; + public Oauth2CredentialsRequestFactory(JwsSignerProvider jwsSignerProvider, Clock clock, Vault vault) { + this.jwsSignerProvider = jwsSignerProvider; this.clock = clock; this.vault = vault; - this.monitor = monitor; } /** @@ -104,18 +101,13 @@ private Result createSharedSecretRequest(DataAddress d @NotNull private Result createAssertion(String pkSecret, DataAddress dataAddress) { - var privateKey = privateKeyResolver.resolvePrivateKey(pkSecret); - if (privateKey.failed()) { - return Result.failure("Failed to resolve private key with alias: " + pkSecret); - } - var validity = Optional.ofNullable(dataAddress.getStringProperty(VALIDITY)) .map(this::parseLong) .orElse(DEFAULT_TOKEN_VALIDITY); var decorator = new Oauth2AssertionDecorator(dataAddress.getStringProperty(TOKEN_URL), dataAddress.getStringProperty(CLIENT_ID), clock, validity); - var service = new JwtGenerationService(); + var service = new JwtGenerationService(jwsSignerProvider); - return service.generate(privateKey::getContent, decorator); + return service.generate(pkSecret, decorator); } @Nullable diff --git a/extensions/data-plane/data-plane-http-oauth2-core/src/test/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactoryTest.java b/extensions/data-plane/data-plane-http-oauth2-core/src/test/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactoryTest.java index 9ff491b5aeb..8644dbf797a 100644 --- a/extensions/data-plane/data-plane-http-oauth2-core/src/test/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactoryTest.java +++ b/extensions/data-plane/data-plane-http-oauth2-core/src/test/java/org/eclipse/edc/connector/dataplane/http/oauth2/Oauth2CredentialsRequestFactoryTest.java @@ -15,16 +15,17 @@ package org.eclipse.edc.connector.dataplane.http.oauth2; import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.KeyUse; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; import com.nimbusds.jwt.SignedJWT; +import org.assertj.core.api.AssertionsForClassTypes; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; import org.eclipse.edc.iam.oauth2.spi.client.PrivateKeyOauth2CredentialsRequest; import org.eclipse.edc.iam.oauth2.spi.client.SharedSecretOauth2CredentialsRequest; -import org.eclipse.edc.keys.spi.PrivateKeyResolver; -import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.spi.security.Vault; import org.junit.jupiter.api.Test; @@ -45,6 +46,7 @@ import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.SCOPE; import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.TOKEN_URL; import static org.eclipse.edc.iam.oauth2.spi.Oauth2DataAddressSchema.VALIDITY; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -53,10 +55,9 @@ class Oauth2CredentialsRequestFactoryTest { private final Instant now = Instant.now(); private final Clock clock = Clock.fixed(now, UTC); - private final PrivateKeyResolver privateKeyResolver = mock(PrivateKeyResolver.class); - private final Vault vault = mock(Vault.class); - private final Monitor monitor = mock(Monitor.class); - private final Oauth2CredentialsRequestFactory factory = new Oauth2CredentialsRequestFactory(privateKeyResolver, clock, vault, monitor); + private final JwsSignerProvider privateKeyResolver = mock(); + private final Vault vault = mock(); + private final Oauth2CredentialsRequestFactory factory = new Oauth2CredentialsRequestFactory(privateKeyResolver, clock, vault); @Test void shouldCreateSharedSecretRequestWithKey_whenPrivateKeyNameIsAbsent() { @@ -68,7 +69,7 @@ void shouldCreateSharedSecretRequestWithKey_whenPrivateKeyNameIsAbsent() { var result = factory.create(address); - assertThat(result).matches(Result::succeeded).extracting(Result::getContent) + AssertionsForClassTypes.assertThat(result).matches(Result::succeeded).extracting(Result::getContent) .asInstanceOf(type(SharedSecretOauth2CredentialsRequest.class)) .satisfies(request -> { assertThat(request.getGrantType()).isEqualTo("client_credentials"); @@ -90,13 +91,13 @@ void shouldFail_whenClientSecretCannotBeObtained() { var result = factory.create(address); - assertThat(result).matches(Result::failed); + AssertionsForClassTypes.assertThat(result).matches(Result::failed); } @Test void shouldCreatePrivateKeyRequest_whenPrivateKeyNameIsPresent() throws JOSEException { var keyPair = generateKeyPair(); - when(privateKeyResolver.resolvePrivateKey("pk-test")).thenReturn(Result.success(keyPair.toPrivateKey())); + when(privateKeyResolver.createJwsSigner("pk-test")).thenReturn(Result.success(new RSASSASigner(keyPair.toPrivateKey()))); var address = defaultAddress() .property(PRIVATE_KEY_NAME, "pk-test") @@ -105,7 +106,7 @@ void shouldCreatePrivateKeyRequest_whenPrivateKeyNameIsPresent() throws JOSEExce var result = factory.create(address); - assertThat(result).matches(Result::succeeded).extracting(Result::getContent) + AssertionsForClassTypes.assertThat(result).matches(Result::succeeded).extracting(Result::getContent) .asInstanceOf(type(PrivateKeyOauth2CredentialsRequest.class)) .satisfies(request -> { assertThat(request.getGrantType()).isEqualTo("client_credentials"); @@ -130,7 +131,7 @@ void shouldCreatePrivateKeyRequest_whenPrivateKeyNameIsPresent() throws JOSEExce @Test void shouldFailIfPrivateKeySecretNotFound() { - when(privateKeyResolver.resolvePrivateKey("pk-test")).thenReturn(Result.failure("not found")); + when(privateKeyResolver.createJwsSigner("pk-test")).thenReturn(Result.failure("foo bar")); var address = defaultAddress() .property(PRIVATE_KEY_NAME, "pk-test") @@ -139,8 +140,8 @@ void shouldFailIfPrivateKeySecretNotFound() { var result = factory.create(address); - assertThat(result).matches(Result::failed) - .extracting(Result::getFailureDetail).asString().contains("pk-test"); + assertThat(result).isFailed() + .detail().contains("foo bar"); } private HttpDataAddress.Builder defaultAddress() { diff --git a/settings.gradle.kts b/settings.gradle.kts index 1786b12907e..48a50091d04 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -234,6 +234,7 @@ include(":spi:common:keys-spi") include(":spi:common:identity-did-spi") include(":spi:common:json-ld-spi") include(":spi:common:jwt-spi") +include(":spi:common:jwt-signer-spi") include(":spi:common:token-spi") include(":spi:common:oauth2-spi") include(":spi:common:policy-engine-spi") diff --git a/spi/common/jwt-signer-spi/build.gradle.kts b/spi/common/jwt-signer-spi/build.gradle.kts new file mode 100644 index 00000000000..c9755db9d4c --- /dev/null +++ b/spi/common/jwt-signer-spi/build.gradle.kts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 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 + * + */ + +plugins { + `java-library` + `maven-publish` +} + +dependencies { + api(libs.nimbus.jwt) + api(project(":spi:common:boot-spi")) //Result + implementation(libs.edc.runtime.metamodel) +} + + diff --git a/spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/JwsSignerProvider.java b/spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/JwsSignerProvider.java new file mode 100644 index 00000000000..bd59acc102a --- /dev/null +++ b/spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/JwsSignerProvider.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 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.jwt.signer.spi; + +import com.nimbusds.jose.JWSSigner; +import org.eclipse.edc.runtime.metamodel.annotation.ExtensionPoint; +import org.eclipse.edc.spi.result.Result; + +/** + * A JwsSignerProvider provides a {@link JWSSigner} for a given private key ID. + */ +@ExtensionPoint +@FunctionalInterface +public interface JwsSignerProvider { + Result createJwsSigner(String privateKeyId); +} diff --git a/spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/package-info.java b/spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/package-info.java new file mode 100644 index 00000000000..71e325a1056 --- /dev/null +++ b/spi/common/jwt-signer-spi/src/main/java/org/eclipse/edc/jwt/signer/spi/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 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 + * + */ +@Spi(value = "Implementation SPI that is used to contribute custom JWSSigners to the JwtGenerationService") + +package org.eclipse.edc.jwt.signer.spi; + +import org.eclipse.edc.runtime.metamodel.annotation.Spi; \ No newline at end of file diff --git a/spi/common/token-spi/src/main/java/org/eclipse/edc/token/spi/TokenGenerationService.java b/spi/common/token-spi/src/main/java/org/eclipse/edc/token/spi/TokenGenerationService.java index fc0341349ac..7da83e47f3b 100644 --- a/spi/common/token-spi/src/main/java/org/eclipse/edc/token/spi/TokenGenerationService.java +++ b/spi/common/token-spi/src/main/java/org/eclipse/edc/token/spi/TokenGenerationService.java @@ -17,9 +17,6 @@ import org.eclipse.edc.spi.iam.TokenRepresentation; import org.eclipse.edc.spi.result.Result; -import java.security.PrivateKey; -import java.util.function.Supplier; - /** * Interface for generating token based on a set of claims and header. */ @@ -28,9 +25,8 @@ public interface TokenGenerationService { /** * Generate a signed token based on the request. * - * @param privateKeySupplier A {@link Supplier} that provides the private on-demand. - * Be advised that holding the private key in memory poses a considerable security risk and should be avoided. - * @param decorators an optional list of {@code JwtDecorator} objects to determine the shape of the token, i.e. headers and claims + * @param privateKeyId A String that identifies the private key, e.g. a secret alias in a {@link org.eclipse.edc.spi.security.Vault} + * @param decorators an optional list of {@code JwtDecorator} objects to determine the shape of the token, i.e. headers and claims */ - Result generate(Supplier privateKeySupplier, TokenDecorator... decorators); + Result generate(String privateKeyId, TokenDecorator... decorators); }