From f73a83193e33e053572bf1b705d7eeb1a25b50c0 Mon Sep 17 00:00:00 2001 From: Paul Latzelsperger Date: Thu, 16 Jan 2025 16:20:56 +0100 Subject: [PATCH] feat: add Credential Request API + issuer service skeleton --- .../ParticipantContextExtension.java | 3 - .../issuerservice-base-bom/build.gradle.kts | 42 ++ dist/bom/issuerservice-bom/build.gradle.kts | 26 + .../build.gradle.kts | 38 ++ .../eclipse/edc/test/bom/BomSmokeTests.java | 24 + .../credential-request-api/build.gradle.kts | 44 ++ .../CredentialRequestApiExtension.java | 92 +++ .../v1alpha/api/ApiSchema.java | 36 ++ .../v1alpha/api/CredentialRequestApi.java | 59 ++ .../api/CredentialRequestApiController.java | 37 ++ .../v1alpha/model/CredentialRequest.java | 22 + .../model/CredentialRequestMessage.java | 84 +++ ...rg.eclipse.edc.spi.system.ServiceExtension | 15 + .../credential-request-api-version.json | 8 + .../build.gradle.kts | 44 ++ ...rg.eclipse.edc.spi.system.ServiceExtension | 13 + ...credential-request-status-api-version.json | 8 + .../dcp/issuer-metadata-api/build.gradle.kts | 45 ++ ...rg.eclipse.edc.spi.system.ServiceExtension | 13 + .../issuer-metadata-api-version.json | 8 + .../openapi/credential-request-api.version | 1 + .../credential-request-status-api.version | 1 + resources/openapi/issuer-metadata-api.version | 1 + resources/openapi/presentation-api.version | 2 +- .../credential-request-api.yaml | 100 ++++ .../openapi/yaml/identity-api/did-api.yaml | 481 ++++++++++++++++ .../yaml/identity-api/keypair-api.yaml | 424 ++++++++++++++ .../identity-api/participant-context-api.yaml | 436 +++++++++++++++ .../verifiable-credentials-api.yaml | 523 ++++++++++++++++++ .../credential-request-status-api.yaml | 203 +++++++ .../presentation-api/issuer-metadata-api.yaml | 203 +++++++ .../presentation-api/presentation-api.yaml | 203 +++++++ settings.gradle.kts | 12 +- .../spi/webcontext/IdentityHubApiContext.java | 1 + 34 files changed, 3246 insertions(+), 6 deletions(-) create mode 100644 dist/bom/issuerservice-base-bom/build.gradle.kts create mode 100644 dist/bom/issuerservice-bom/build.gradle.kts create mode 100644 dist/bom/issuerservice-feature-sql-bom/build.gradle.kts create mode 100644 extensions/protocols/dcp/credential-request-api/build.gradle.kts create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/CredentialRequestApiExtension.java create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/ApiSchema.java create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApi.java create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApiController.java create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequest.java create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequestMessage.java create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 extensions/protocols/dcp/credential-request-api/src/main/resources/credential-request-api-version.json create mode 100644 extensions/protocols/dcp/credential-request-status-api/build.gradle.kts create mode 100644 extensions/protocols/dcp/credential-request-status-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 extensions/protocols/dcp/credential-request-status-api/src/main/resources/credential-request-status-api-version.json create mode 100644 extensions/protocols/dcp/issuer-metadata-api/build.gradle.kts create mode 100644 extensions/protocols/dcp/issuer-metadata-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension create mode 100644 extensions/protocols/dcp/issuer-metadata-api/src/main/resources/issuer-metadata-api-version.json create mode 100644 resources/openapi/credential-request-api.version create mode 100644 resources/openapi/credential-request-status-api.version create mode 100644 resources/openapi/issuer-metadata-api.version create mode 100644 resources/openapi/yaml/credential-request-api/credential-request-api.yaml create mode 100644 resources/openapi/yaml/identity-api/did-api.yaml create mode 100644 resources/openapi/yaml/identity-api/keypair-api.yaml create mode 100644 resources/openapi/yaml/identity-api/participant-context-api.yaml create mode 100644 resources/openapi/yaml/identity-api/verifiable-credentials-api.yaml create mode 100644 resources/openapi/yaml/presentation-api/credential-request-status-api.yaml create mode 100644 resources/openapi/yaml/presentation-api/issuer-metadata-api.yaml create mode 100644 resources/openapi/yaml/presentation-api/presentation-api.yaml diff --git a/core/identity-hub-participants/src/main/java/org/eclipse/edc/identityhub/participantcontext/ParticipantContextExtension.java b/core/identity-hub-participants/src/main/java/org/eclipse/edc/identityhub/participantcontext/ParticipantContextExtension.java index e8bb12f05..c67410171 100644 --- a/core/identity-hub-participants/src/main/java/org/eclipse/edc/identityhub/participantcontext/ParticipantContextExtension.java +++ b/core/identity-hub-participants/src/main/java/org/eclipse/edc/identityhub/participantcontext/ParticipantContextExtension.java @@ -15,7 +15,6 @@ package org.eclipse.edc.identityhub.participantcontext; import org.eclipse.edc.identityhub.spi.did.store.DidResourceStore; -import org.eclipse.edc.identityhub.spi.keypair.KeyPairService; import org.eclipse.edc.identityhub.spi.participantcontext.ParticipantContextService; import org.eclipse.edc.identityhub.spi.participantcontext.StsAccountProvisioner; import org.eclipse.edc.identityhub.spi.participantcontext.events.ParticipantContextObservable; @@ -43,8 +42,6 @@ public class ParticipantContextExtension implements ServiceExtension { @Inject private TransactionContext transactionContext; @Inject - private KeyPairService keyPairService; - @Inject private Clock clock; @Inject private EventRouter eventRouter; diff --git a/dist/bom/issuerservice-base-bom/build.gradle.kts b/dist/bom/issuerservice-base-bom/build.gradle.kts new file mode 100644 index 000000000..8661203bc --- /dev/null +++ b/dist/bom/issuerservice-base-bom/build.gradle.kts @@ -0,0 +1,42 @@ +/* + * 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 + * + */ + +plugins { + `java-library` +} + +dependencies { + runtimeOnly(project(":core:identity-hub-did")) + runtimeOnly(project(":core:identity-hub-core")) + runtimeOnly(project(":core:identity-hub-participants")) + runtimeOnly(project(":core:identity-hub-keypairs")) + runtimeOnly(project(":extensions:did:local-did-publisher")) + // API modules + runtimeOnly(project(":extensions:protocols:dcp:credential-request-api")) + runtimeOnly(project(":extensions:protocols:dcp:credential-request-status-api")) + runtimeOnly(project(":extensions:protocols:dcp:issuer-metadata-api")) + + runtimeOnly(project(":extensions:sts:sts-account-provisioner")) + runtimeOnly(libs.edc.identity.did.core) + runtimeOnly(libs.edc.core.token) + runtimeOnly(libs.edc.api.version) + runtimeOnly(libs.edc.transaction.local) // needed by the PresentationCreatorRegistry + + runtimeOnly(libs.edc.identity.did.web) + runtimeOnly(libs.bundles.connector) +} + +edcBuild { + +} \ No newline at end of file diff --git a/dist/bom/issuerservice-bom/build.gradle.kts b/dist/bom/issuerservice-bom/build.gradle.kts new file mode 100644 index 000000000..af6567ab3 --- /dev/null +++ b/dist/bom/issuerservice-bom/build.gradle.kts @@ -0,0 +1,26 @@ +/* + * 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 + * + */ + +plugins { + `java-library` +} + +dependencies { + runtimeOnly(project(":dist:bom:issuerservice-base-bom")) + runtimeOnly(project(":extensions:sts:sts-account-service-remote")) +} + +edcBuild { + +} \ No newline at end of file diff --git a/dist/bom/issuerservice-feature-sql-bom/build.gradle.kts b/dist/bom/issuerservice-feature-sql-bom/build.gradle.kts new file mode 100644 index 000000000..b8069f99b --- /dev/null +++ b/dist/bom/issuerservice-feature-sql-bom/build.gradle.kts @@ -0,0 +1,38 @@ +/* + * 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 + * + */ + +plugins { + `java-library` +} + +dependencies { + // sql modules + api(project(":extensions:store:sql:identity-hub-credentials-store-sql")) + api(project(":extensions:store:sql:identity-hub-did-store-sql")) + api(project(":extensions:store:sql:identity-hub-keypair-store-sql")) + api(project(":extensions:store:sql:identity-hub-participantcontext-store-sql")) + + api(libs.edc.sql.core) + api(libs.edc.transaction.local) + api(libs.edc.sql.pool) + api(libs.edc.sql.bootstrapper) + api(libs.edc.sql.jtivdalidation) + + // third-party deps + api(libs.postgres) +} + +edcBuild { + +} \ No newline at end of file diff --git a/e2e-tests/bom-tests/src/test/java/org/eclipse/edc/test/bom/BomSmokeTests.java b/e2e-tests/bom-tests/src/test/java/org/eclipse/edc/test/bom/BomSmokeTests.java index 611d6f7a9..38c5d08f6 100644 --- a/e2e-tests/bom-tests/src/test/java/org/eclipse/edc/test/bom/BomSmokeTests.java +++ b/e2e-tests/bom-tests/src/test/java/org/eclipse/edc/test/bom/BomSmokeTests.java @@ -109,4 +109,28 @@ class IdentityHubWithSts extends SmokeTest { ":dist:bom:identityhub-with-sts-bom" )); } + + @Nested + @EndToEndTest + class IssuerService extends SmokeTest { + @RegisterExtension + protected RuntimeExtension runtime = + new RuntimePerMethodExtension(new EmbeddedRuntime("issuer-service-bom", + new HashMap<>() { + { + put("web.http.port", DEFAULT_PORT); + put("web.http.path", DEFAULT_PATH); + put("web.http.version.port", valueOf(getFreePort())); + put("web.http.version.path", "/api/version"); + put("web.http.did.port", valueOf(getFreePort())); + put("web.http.did.path", "/api/did"); + put("web.http.credential-request.port", valueOf(getFreePort())); + put("web.http.credential-request.path", "/api/issuance"); + put("edc.sts.account.api.url", "https://sts.com/accounts"); + put("edc.sts.accounts.api.auth.header.value", "password"); + } + }, + ":dist:bom:issuerservice-bom" + )); + } } diff --git a/extensions/protocols/dcp/credential-request-api/build.gradle.kts b/extensions/protocols/dcp/credential-request-api/build.gradle.kts new file mode 100644 index 000000000..e995d60e9 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/build.gradle.kts @@ -0,0 +1,44 @@ +/* + * 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 + * + */ + +plugins { + `java-library` + `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + api(project(":spi:identity-hub-spi")) + api(project(":spi:verifiable-credential-spi")) + api(libs.edc.spi.jsonld) + api(libs.edc.spi.jwt) + api(libs.edc.spi.core) + implementation(libs.edc.spi.web) + implementation(libs.edc.spi.dcp) + implementation(libs.edc.lib.jerseyproviders) + implementation(libs.edc.lib.transform) + implementation(libs.edc.dcp.transform) + implementation(libs.jakarta.rsApi) + testImplementation(libs.edc.junit) + testImplementation(libs.edc.jsonld) + testImplementation(testFixtures(libs.edc.core.jersey)) + testImplementation(testFixtures(project(":spi:verifiable-credential-spi"))) + testImplementation(libs.nimbus.jwt) +} + +edcBuild { + swagger { + apiGroup.set("credential-request-api") + } +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/CredentialRequestApiExtension.java b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/CredentialRequestApiExtension.java new file mode 100644 index 000000000..b910dac6d --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/CredentialRequestApiExtension.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2025 Cofinity-X + * + * 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: + * Cofinity-X - initial API and implementation + * + */ + +package org.eclipse.edc.protocols.dcp.credentialrequest; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.api.CredentialRequestApiController; +import org.eclipse.edc.runtime.metamodel.annotation.Configuration; +import org.eclipse.edc.runtime.metamodel.annotation.Extension; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.runtime.metamodel.annotation.Setting; +import org.eclipse.edc.runtime.metamodel.annotation.Settings; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.apiversion.ApiVersionService; +import org.eclipse.edc.spi.system.apiversion.VersionRecord; +import org.eclipse.edc.spi.types.TypeManager; +import org.eclipse.edc.web.spi.WebService; +import org.eclipse.edc.web.spi.configuration.PortMapping; +import org.eclipse.edc.web.spi.configuration.PortMappingRegistry; + +import java.io.IOException; +import java.util.stream.Stream; + +import static org.eclipse.edc.identityhub.spi.webcontext.IdentityHubApiContext.CREDENTIAL_REQUEST; +import static org.eclipse.edc.protocols.dcp.credentialrequest.CredentialRequestApiExtension.NAME; + +@Extension(value = NAME) +public class CredentialRequestApiExtension implements ServiceExtension { + public static final String NAME = "CredentialRequestApiExtension"; + + private static final String API_VERSION_JSON_FILE = "credential-request-api-version.json"; + + @Inject + private TypeManager typeManager; + @Inject + private ApiVersionService apiVersionService; + @Inject + private WebService webService; + @Inject + private PortMappingRegistry portMappingRegistry; + + @Configuration + private CredentialRequestApiConfiguration apiConfiguration; + + @Override + public void initialize(ServiceExtensionContext context) { + + portMappingRegistry.register(new PortMapping(CREDENTIAL_REQUEST, apiConfiguration.port(), apiConfiguration.path())); + + var controller = new CredentialRequestApiController(); + webService.registerResource(CREDENTIAL_REQUEST, controller); + + registerVersionInfo(getClass().getClassLoader()); + } + + private void registerVersionInfo(ClassLoader resourceClassLoader) { + try (var versionContent = resourceClassLoader.getResourceAsStream(API_VERSION_JSON_FILE)) { + if (versionContent == null) { + throw new EdcException("Version file '%s' not found or not readable.".formatted(API_VERSION_JSON_FILE)); + } + Stream.of(typeManager.getMapper() + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .readValue(versionContent, VersionRecord[].class)) + .forEach(vr -> apiVersionService.addRecord("credential-request", vr)); + } catch (IOException e) { + throw new EdcException(e); + } + } + + @Settings + record CredentialRequestApiConfiguration( + @Setting(key = "web.http." + CREDENTIAL_REQUEST + ".port", description = "Port for " + CREDENTIAL_REQUEST + " api context", defaultValue = 13132 + "") + int port, + @Setting(key = "web.http." + CREDENTIAL_REQUEST + ".path", description = "Path for " + CREDENTIAL_REQUEST + " api context", defaultValue = "/api/issuance") + String path + ) { + + } +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/ApiSchema.java b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/ApiSchema.java new file mode 100644 index 000000000..cd1940289 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/ApiSchema.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Cofinity-X + * + * 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: + * Cofinity-X - initial API and implementation + * + */ + +package org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.api; + +import io.swagger.v3.oas.annotations.media.Schema; + +public interface ApiSchema { + @Schema(name = "ApiErrorDetail", example = ApiErrorDetailSchema.API_ERROR_EXAMPLE) + record ApiErrorDetailSchema( + String message, + String type, + String path, + String invalidValue + ) { + public static final String API_ERROR_EXAMPLE = """ + { + "message": "error message", + "type": "ErrorType", + "path": "object.error.path", + "invalidValue": "this value is not valid" + } + """; + } +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApi.java b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApi.java new file mode 100644 index 000000000..b655b1fb0 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApi.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Cofinity-X + * + * 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: + * Cofinity-X - initial API and implementation + * + */ + +package org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.api; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.core.Response; +import org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.model.CredentialRequestMessage; + +@OpenAPIDefinition( + info = @Info(description = "This represents the Credential Request API as per DCP specification. It serves endpoints to request the issuance of Verifiable Credentials from an issuer.", title = "Credential Request API", + version = "v1alpha")) +@SecurityScheme(name = "Authentication", + description = "Self-Issued ID token containing an access_token", + type = SecuritySchemeType.HTTP, + scheme = "bearer", + bearerFormat = "JWT") +public interface CredentialRequestApi { + + @Tag(name = "Credential Request API") + @Operation(description = "Requests the issuance of one or several verifiable credentials from an issuer", + operationId = "requestCredentials", + requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = CredentialRequestMessage.class))), + responses = { + @ApiResponse(responseCode = "201", description = "The request was successfully received and is being processed.", headers = {@Header(name = "Location", + description = "contains the relative URL where the status of the request can be queried (Credential Request Status API)")}), + @ApiResponse(responseCode = "400", description = "Request body was malformed, e.g. required parameter or properties were missing", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "401", description = "No Authorization header was provided.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiSchema.ApiErrorDetailSchema.class)))), + @ApiResponse(responseCode = "403", description = "The given authentication token could not be validated or the client is not authorized to call this endpoint.", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiSchema.ApiErrorDetailSchema.class)))) + + } + ) + Response requestCredential(CredentialRequestMessage message); +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApiController.java b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApiController.java new file mode 100644 index 000000000..6b2fb1205 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/api/CredentialRequestApiController.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Cofinity-X + * + * 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: + * Cofinity-X - initial API and implementation + * + */ + +package org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.api; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Response; +import org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.model.CredentialRequestMessage; + +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +@Consumes(APPLICATION_JSON) +@Produces(APPLICATION_JSON) +@Path("/v1alpha/credentials") +public class CredentialRequestApiController implements CredentialRequestApi { + + @POST + @Path("/") + @Override + public Response requestCredential(CredentialRequestMessage message) { + return null; + } +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequest.java b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequest.java new file mode 100644 index 000000000..7ee0263d3 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequest.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Cofinity-X + * + * 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: + * Cofinity-X - initial API and implementation + * + */ + +package org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record CredentialRequest(@JsonProperty("credentialType") String credentialType, + @JsonProperty("format") String format, + @JsonProperty("payload") Object payload) { +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequestMessage.java b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequestMessage.java new file mode 100644 index 000000000..a93f2de9b --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/java/org/eclipse/edc/protocols/dcp/credentialrequest/v1alpha/model/CredentialRequestMessage.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025 Cofinity-X + * + * 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: + * Cofinity-X - initial API and implementation + * + */ + +package org.eclipse.edc.protocols.dcp.credentialrequest.v1alpha.model; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; + +import java.util.List; + +/** + * Represents a request made by a credential holder to a credential issuer to request the issuance of one or several + * Verifiable Credentials + */ +@JsonDeserialize(builder = CredentialRequestMessage.Builder.class) +public class CredentialRequestMessage { + private List credentials = List.of(); + private List contexts = List.of("https://w3id.org/dspace-dcp/v1.0/dcp.jsonld"); + private String type = "CredentialRequestMessage"; + + public String getType() { + return type; + } + + public List getContexts() { + return contexts; + } + + public List getCredentials() { + return credentials; + } + + @JsonPOJOBuilder(withPrefix = "") + public static final class Builder { + + private final CredentialRequestMessage instance; + + private Builder() { + instance = new CredentialRequestMessage(); + } + + public static Builder newInstance() { + return new Builder(); + } + + public Builder contexts(List contexts) { + this.instance.contexts = contexts; + return this; + } + + public Builder type(String type) { + this.instance.type = type; + return this; + } + + public Builder credentials(List credentials) { + this.instance.credentials = credentials; + return this; + } + + public Builder credential(CredentialRequest credential) { + this.instance.credentials.add(credential); + return this; + } + + public CredentialRequestMessage build() { + if (instance.contexts == null || instance.contexts.isEmpty()) { + throw new IllegalArgumentException("@context object cannot be null or empty"); + } + return instance; + } + } +} diff --git a/extensions/protocols/dcp/credential-request-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/protocols/dcp/credential-request-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..e7a55dd33 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2025 Cofinity-X +# +# 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: +# Cofinity-X - initial API and implementation +# +# + +org.eclipse.edc.protocols.dcp.credentialrequest.CredentialRequestApiExtension \ No newline at end of file diff --git a/extensions/protocols/dcp/credential-request-api/src/main/resources/credential-request-api-version.json b/extensions/protocols/dcp/credential-request-api/src/main/resources/credential-request-api-version.json new file mode 100644 index 000000000..138694c69 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-api/src/main/resources/credential-request-api-version.json @@ -0,0 +1,8 @@ +[ + { + "version": "1.0.0", + "urlPath": "/v1alpha", + "lastUpdated": "2025-01-16T12:00:00Z", + "maturity": null + } +] \ No newline at end of file diff --git a/extensions/protocols/dcp/credential-request-status-api/build.gradle.kts b/extensions/protocols/dcp/credential-request-status-api/build.gradle.kts new file mode 100644 index 000000000..4f7c84bcd --- /dev/null +++ b/extensions/protocols/dcp/credential-request-status-api/build.gradle.kts @@ -0,0 +1,44 @@ +/* + * 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 + * + */ + +plugins { + `java-library` + `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + api(project(":spi:identity-hub-spi")) + api(project(":spi:verifiable-credential-spi")) + api(libs.edc.spi.jsonld) + api(libs.edc.spi.jwt) + api(libs.edc.spi.core) + implementation(libs.edc.spi.web) + implementation(libs.edc.spi.dcp) + implementation(libs.edc.lib.jerseyproviders) + implementation(libs.edc.lib.transform) + implementation(libs.edc.dcp.transform) + implementation(libs.jakarta.rsApi) + testImplementation(libs.edc.junit) + testImplementation(libs.edc.jsonld) + testImplementation(testFixtures(libs.edc.core.jersey)) + testImplementation(testFixtures(project(":spi:verifiable-credential-spi"))) + testImplementation(libs.nimbus.jwt) +} + +edcBuild { + swagger { + apiGroup.set("credential-request-status-api") + } +} diff --git a/extensions/protocols/dcp/credential-request-status-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/protocols/dcp/credential-request-status-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..ed393669f --- /dev/null +++ b/extensions/protocols/dcp/credential-request-status-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,13 @@ +# +# 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 +# +# diff --git a/extensions/protocols/dcp/credential-request-status-api/src/main/resources/credential-request-status-api-version.json b/extensions/protocols/dcp/credential-request-status-api/src/main/resources/credential-request-status-api-version.json new file mode 100644 index 000000000..138694c69 --- /dev/null +++ b/extensions/protocols/dcp/credential-request-status-api/src/main/resources/credential-request-status-api-version.json @@ -0,0 +1,8 @@ +[ + { + "version": "1.0.0", + "urlPath": "/v1alpha", + "lastUpdated": "2025-01-16T12:00:00Z", + "maturity": null + } +] \ No newline at end of file diff --git a/extensions/protocols/dcp/issuer-metadata-api/build.gradle.kts b/extensions/protocols/dcp/issuer-metadata-api/build.gradle.kts new file mode 100644 index 000000000..0c4423757 --- /dev/null +++ b/extensions/protocols/dcp/issuer-metadata-api/build.gradle.kts @@ -0,0 +1,45 @@ +/* + * 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 + * + */ + +plugins { + `java-library` + `maven-publish` + id("io.swagger.core.v3.swagger-gradle-plugin") +} + +dependencies { + api(project(":spi:identity-hub-spi")) + api(project(":spi:verifiable-credential-spi")) + api(libs.edc.spi.jsonld) + api(libs.edc.spi.jwt) + api(libs.edc.spi.core) + implementation(libs.edc.spi.validator) + implementation(libs.edc.spi.web) + implementation(libs.edc.spi.dcp) + implementation(libs.edc.lib.jerseyproviders) + implementation(libs.edc.lib.transform) + implementation(libs.edc.dcp.transform) + implementation(libs.jakarta.rsApi) + testImplementation(libs.edc.junit) + testImplementation(libs.edc.jsonld) + testImplementation(testFixtures(libs.edc.core.jersey)) + testImplementation(testFixtures(project(":spi:verifiable-credential-spi"))) + testImplementation(libs.nimbus.jwt) +} + +edcBuild { + swagger { + apiGroup.set("presentation-api") + } +} diff --git a/extensions/protocols/dcp/issuer-metadata-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/protocols/dcp/issuer-metadata-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 000000000..ed393669f --- /dev/null +++ b/extensions/protocols/dcp/issuer-metadata-api/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1,13 @@ +# +# 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 +# +# diff --git a/extensions/protocols/dcp/issuer-metadata-api/src/main/resources/issuer-metadata-api-version.json b/extensions/protocols/dcp/issuer-metadata-api/src/main/resources/issuer-metadata-api-version.json new file mode 100644 index 000000000..138694c69 --- /dev/null +++ b/extensions/protocols/dcp/issuer-metadata-api/src/main/resources/issuer-metadata-api-version.json @@ -0,0 +1,8 @@ +[ + { + "version": "1.0.0", + "urlPath": "/v1alpha", + "lastUpdated": "2025-01-16T12:00:00Z", + "maturity": null + } +] \ No newline at end of file diff --git a/resources/openapi/credential-request-api.version b/resources/openapi/credential-request-api.version new file mode 100644 index 000000000..543341f5d --- /dev/null +++ b/resources/openapi/credential-request-api.version @@ -0,0 +1 @@ +extensions/protocols/dcp/credential-request-api/src/main/resources/credential-request-api-version.json \ No newline at end of file diff --git a/resources/openapi/credential-request-status-api.version b/resources/openapi/credential-request-status-api.version new file mode 100644 index 000000000..9a6c4fcc4 --- /dev/null +++ b/resources/openapi/credential-request-status-api.version @@ -0,0 +1 @@ +extensions/protocols/dcp/credential-request-status-api/src/main/resources/credential-request-status-api-version.json \ No newline at end of file diff --git a/resources/openapi/issuer-metadata-api.version b/resources/openapi/issuer-metadata-api.version new file mode 100644 index 000000000..e126bc29e --- /dev/null +++ b/resources/openapi/issuer-metadata-api.version @@ -0,0 +1 @@ +extensions/protocols/dcp/issuer-metadata-api/src/main/resources/issuer-metadata-api-version.json \ No newline at end of file diff --git a/resources/openapi/presentation-api.version b/resources/openapi/presentation-api.version index 0d7896eb5..6450a7022 100644 --- a/resources/openapi/presentation-api.version +++ b/resources/openapi/presentation-api.version @@ -1 +1 @@ -core/presentation-api/src/main/resources/presentation-api-version.json \ No newline at end of file +extensions/protocols/dcp/presentation-api/src/main/resources/presentation-api-version.json \ No newline at end of file diff --git a/resources/openapi/yaml/credential-request-api/credential-request-api.yaml b/resources/openapi/yaml/credential-request-api/credential-request-api.yaml new file mode 100644 index 000000000..3a9ee0c8a --- /dev/null +++ b/resources/openapi/yaml/credential-request-api/credential-request-api.yaml @@ -0,0 +1,100 @@ +openapi: 3.0.1 +info: + description: This represents the Credential Request API as per DCP specification. + It serves endpoints to request the issuance of Verifiable Credentials from an + issuer. + title: Credential Request API + version: v1alpha +paths: + /v1alpha/credentials: + post: + description: Requests the issuance of one or several verifiable credentials + from an issuer + operationId: requestCredentials + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialRequestMessage" + responses: + "201": + description: The request was successfully received and is being processed. + headers: + Location: + description: contains the relative URL where the status of the request + can be queried (Credential Request Status API) + style: simple + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, e.g. required parameter or properties\ + \ were missing" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: No Authorization header was provided. + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The given authentication token could not be validated or the + client is not authorized to call this endpoint. + tags: + - Credential Request API +components: + schemas: + ApiErrorDetail: + type: object + example: + message: error message + type: ErrorType + path: object.error.path + invalidValue: this value is not valid + properties: + invalidValue: + type: string + message: + type: string + path: + type: string + type: + type: string + CredentialRequest: + type: object + properties: + credentialType: + type: string + format: + type: string + payload: + type: object + CredentialRequestMessage: + type: object + properties: + contexts: + type: array + items: + type: string + credentials: + type: array + items: + $ref: "#/components/schemas/CredentialRequest" + type: + type: string + securitySchemes: + Authentication: + bearerFormat: JWT + description: Self-Issued ID token containing an access_token + scheme: bearer + type: http diff --git a/resources/openapi/yaml/identity-api/did-api.yaml b/resources/openapi/yaml/identity-api/did-api.yaml new file mode 100644 index 000000000..1a99097a8 --- /dev/null +++ b/resources/openapi/yaml/identity-api/did-api.yaml @@ -0,0 +1,481 @@ +openapi: 3.0.1 +info: + description: This is the Identity API for DID documents + title: DID Identity API + version: "1" +paths: + /v1alpha/dids: + get: + description: Get all DID documents across all Participant Contexts. Requires + elevated access. + operationId: getAllDids + parameters: + - in: query + name: offset + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + schema: + type: integer + format: int32 + default: 50 + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DidDocument" + description: The list of DID Documents. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The query was malformed or was not understood by the server. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + tags: + - DID + /v1alpha/participants/{participantContextId}/dids/publish: + post: + description: Publish an (existing) DID document. The DID is expected to exist + in the database. + operationId: publishDid + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DidRequestPayload" + responses: + "200": + description: The DID document was successfully published. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The DID could not be published because it does not exist. + tags: + - DID + /v1alpha/participants/{participantContextId}/dids/query: + post: + description: Query for DID documents. + operationId: queryDids + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/QuerySpec" + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DidDocument" + description: The list of DID Documents. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The query was malformed or was not understood by the server. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + tags: + - DID + /v1alpha/participants/{participantContextId}/dids/state: + post: + description: Get state of a DID document + operationId: getDidState + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DidRequestPayload" + responses: + "200": + description: The DID state was successfully obtained + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The DID does not exist. + tags: + - DID + /v1alpha/participants/{participantContextId}/dids/unpublish: + post: + description: Un-Publish an (existing) DID document. The DID is expected to exist + in the database. + operationId: unpublishDid + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DidRequestPayload" + responses: + "200": + description: The DID document was successfully un-published. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The DID could not be unpublished because the underlying VDR + does not support un-publishing. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The DID could not be un-published because it does not exist. + tags: + - DID + /v1alpha/participants/{participantContextId}/dids/{did}/endpoints: + delete: + description: Removes a service endpoint from a particular DID document. + operationId: deleteDidEndpoint + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: did + required: true + schema: + type: string + - in: query + name: serviceId + schema: + type: string + - in: query + name: autoPublish + schema: + type: boolean + responses: + "200": + description: The DID document was successfully updated. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The DID document could not be updated, because a service endpoint\ + \ with the same ID already exists." + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The service endpoint could not be added because the DID does + not exist. + tags: + - DID + patch: + description: Replaces a service endpoint of a particular DID document. + operationId: replaceDidEndpoint + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: did + required: true + schema: + type: string + - in: query + name: autoPublish + schema: + type: boolean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + responses: + "200": + description: The DID document was successfully updated. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The DID document could not be updated, because a service endpoint\ + \ with the given ID does not exist." + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The service endpoint could not be replaced because the DID + does not exist. + tags: + - DID + post: + description: Adds a service endpoint to a particular DID document. + operationId: addDidEndpoint + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: did + required: true + schema: + type: string + - in: query + name: autoPublish + schema: + type: boolean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Service" + responses: + "200": + description: The DID document was successfully updated. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The service endpoint could not be added because the DID does + not exist. + "409": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The DID document could not be updated, because a service endpoint\ + \ with the same ID already exists." + tags: + - DID +components: + schemas: + ApiErrorDetail: + type: object + properties: + invalidValue: + type: object + message: + type: string + path: + type: string + type: + type: string + Criterion: + type: object + properties: + operandLeft: + type: object + operandRight: + type: object + operator: + type: string + DidDocument: + type: object + properties: + '@context': + type: array + items: + type: object + authentication: + type: array + items: + type: string + id: + type: string + service: + type: array + items: + $ref: "#/components/schemas/Service" + verificationMethod: + type: array + items: + $ref: "#/components/schemas/VerificationMethod" + DidRequestPayload: + type: object + properties: + did: + type: string + QuerySpec: + type: object + properties: + filterExpression: + type: array + items: + $ref: "#/components/schemas/Criterion" + limit: + type: integer + format: int32 + offset: + type: integer + format: int32 + sortField: + type: string + sortOrder: + type: string + enum: + - ASC + - DESC + Service: + type: object + properties: + id: + type: string + serviceEndpoint: + type: string + type: + type: string + VerificationMethod: + type: object + properties: + controller: + type: string + id: + type: string + publicKeyJwk: + type: object + additionalProperties: + type: object + publicKeyMultibase: + type: string + type: + type: string diff --git a/resources/openapi/yaml/identity-api/keypair-api.yaml b/resources/openapi/yaml/identity-api/keypair-api.yaml new file mode 100644 index 000000000..87cc48e6b --- /dev/null +++ b/resources/openapi/yaml/identity-api/keypair-api.yaml @@ -0,0 +1,424 @@ +openapi: 3.0.1 +info: + description: This is the Identity API for manipulating KeyPairResources + title: KeyPairResources Identity API + version: "1" +paths: + /v1alpha/keypairs: + get: + description: Get all KeyPair resources across all Participant Contexts. Requires + elevated access. + operationId: getAllKeyPairs + parameters: + - in: query + name: offset + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + schema: + type: integer + format: int32 + default: 50 + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/KeyPairResource" + description: The list of KeyPair resources. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The query was malformed or was not understood by the server. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + tags: + - Key Pairs + /v1alpha/participants/{participantContextId}/keypairs: + get: + description: Finds all KeyPairResources for a particular ParticipantContext. + operationId: queryKeyPairByParticipantId + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/KeyPairResource" + description: The KeyPairResource. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A KeyPairResource with the given ID does not exist. + tags: + - Key Pairs + put: + description: "Adds a new key pair to a ParticipantContext. Note that the key\ + \ pair is either generated, or the private key is expected to be found in\ + \ the vault." + operationId: addKeyPair + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + - in: query + name: makeDefault + schema: + type: boolean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/KeyDescriptor" + responses: + "200": + description: The KeyPairResource was successfully created and linked to + the participant. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A KeyPairResource with the given ID does not exist. + tags: + - Key Pairs + /v1alpha/participants/{participantContextId}/keypairs/{keyPairId}: + get: + description: Finds a KeyPairResource by ID. + operationId: getKeyPair + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: keyPairId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/KeyPairResource" + description: The KeyPairResource. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A KeyPairResource with the given ID does not exist. + tags: + - Key Pairs + /v1alpha/participants/{participantContextId}/keypairs/{keyPairId}/activate: + post: + description: Sets a KeyPairResource to the ACTIVE state. Will fail if the current + state is anything other than ACTIVE or CREATED. + operationId: activateKeyPair + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: keyPairId + required: true + schema: + type: string + responses: + "200": + description: The KeyPairResource. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed." + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A KeyPairResource with the given ID does not exist. + tags: + - Key Pairs + /v1alpha/participants/{participantContextId}/keypairs/{keyPairId}/revoke: + post: + description: "Revokes (=removes) a particular key pair, identified by their\ + \ ID and create a new successor key." + operationId: revokeKeyPair + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: keyPairId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/KeyDescriptor" + responses: + "200": + description: The KeyPairResource was successfully rotated and linked to + the participant. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A KeyPairResource with the given ID does not exist. + tags: + - Key Pairs + /v1alpha/participants/{participantContextId}/keypairs/{keyPairId}/rotate: + post: + description: "Rotates (=retires) a particular key pair, identified by their\ + \ ID and optionally create a new successor key." + operationId: rotateKeyPair + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: keyPairId + required: true + schema: + type: string + - in: query + name: duration + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/KeyDescriptor" + responses: + "200": + description: The KeyPairResource was successfully rotated and linked to + the participant. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A KeyPairResource with the given ID does not exist. + tags: + - Key Pairs +components: + schemas: + ApiErrorDetail: + type: object + properties: + invalidValue: + type: object + message: + type: string + path: + type: string + type: + type: string + KeyDescriptor: + type: object + properties: + active: + type: boolean + keyGeneratorParams: + type: object + additionalProperties: + type: object + keyId: + type: string + privateKeyAlias: + type: string + publicKeyJwk: + type: object + additionalProperties: + type: object + publicKeyPem: + type: string + resourceId: + type: string + type: + type: string + KeyPairResource: + type: object + properties: + defaultPair: + type: boolean + groupName: + type: string + id: + type: string + keyContext: + type: string + keyId: + type: string + participantContextId: + type: string + privateKeyAlias: + type: string + rotationDuration: + type: integer + format: int64 + serializedPublicKey: + type: string + state: + type: integer + format: int32 + timestamp: + type: integer + format: int64 + useDuration: + type: integer + format: int64 diff --git a/resources/openapi/yaml/identity-api/participant-context-api.yaml b/resources/openapi/yaml/identity-api/participant-context-api.yaml new file mode 100644 index 000000000..5c9d02373 --- /dev/null +++ b/resources/openapi/yaml/identity-api/participant-context-api.yaml @@ -0,0 +1,436 @@ +openapi: 3.0.1 +info: + description: This is the Identity API for manipulating ParticipantContexts + title: ParticipantContext Management API + version: "1" +paths: + /v1alpha/participants: + get: + description: Get all DID documents across all Participant Contexts. Requires + elevated access. + operationId: getAllParticipants + parameters: + - in: query + name: offset + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + schema: + type: integer + format: int32 + default: 50 + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ParticipantContext" + description: The list of ParticipantContexts. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The query was malformed or was not understood by the server. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + tags: + - Participant Context + post: + description: Creates a new ParticipantContext object. + operationId: createParticipant + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ParticipantManifest" + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/CreateParticipantContextResponse" + description: "The ParticipantContext was created successfully, its API token\ + \ is returned in the response body." + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "409": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Can't create the ParticipantContext, because a object with\ + \ the same ID already exists" + tags: + - Participant Context + /v1alpha/participants/{participantContextId}: + delete: + description: Delete a ParticipantContext. + operationId: deleteParticipant + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + type: string + description: The ParticipantContext was deleted successfully + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A ParticipantContext with the given ID does not exist. + tags: + - Participant Context + get: + description: Gets ParticipantContexts by ID. + operationId: getParticipant + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ParticipantContext" + description: The list of ParticipantContexts. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A ParticipantContext with the given ID does not exist. + tags: + - Participant Context + /v1alpha/participants/{participantContextId}/roles: + put: + description: "Updates a ParticipantContext's roles. Note that this is an absolute\ + \ update, that means all roles that the Participant should have must be submitted\ + \ in the body. Requires elevated privileges." + operationId: updateParticipantRoles + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: array + items: + type: string + responses: + "200": + description: The ParticipantContext was updated successfully + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A ParticipantContext with the given ID does not exist. + tags: + - Participant Context + /v1alpha/participants/{participantContextId}/state: + post: + description: "Activates a ParticipantContext. This operation is idempotent,\ + \ i.e. activating an already active ParticipantContext is a NOOP." + operationId: activateParticipant + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + - in: query + name: isActive + schema: + type: boolean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ParticipantManifest" + responses: + "200": + content: + application/json: + schema: + type: string + description: The ParticipantContext was activated/deactivated successfully + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A ParticipantContext with the given ID does not exist. + tags: + - Participant Context + /v1alpha/participants/{participantContextId}/token: + post: + description: Regenerates the API token for a ParticipantContext and returns + the new token. + operationId: regenerateParticipantToken + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ParticipantManifest" + responses: + "200": + content: + application/json: + schema: + type: string + description: The API token was regenerated successfully + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A ParticipantContext with the given ID does not exist. + tags: + - Participant Context +components: + schemas: + ApiErrorDetail: + type: object + properties: + invalidValue: + type: object + message: + type: string + path: + type: string + type: + type: string + CreateParticipantContextResponse: + type: object + properties: + apiKey: + type: string + clientId: + type: string + clientSecret: + type: string + KeyDescriptor: + type: object + properties: + active: + type: boolean + keyGeneratorParams: + type: object + additionalProperties: + type: object + keyId: + type: string + privateKeyAlias: + type: string + publicKeyJwk: + type: object + additionalProperties: + type: object + publicKeyPem: + type: string + resourceId: + type: string + type: + type: string + ParticipantContext: + type: object + properties: + apiTokenAlias: + type: string + createdAt: + type: integer + format: int64 + did: + type: string + lastModified: + type: integer + format: int64 + participantContextId: + type: string + roles: + type: array + items: + type: string + state: + type: integer + format: int32 + ParticipantManifest: + type: object + properties: + active: + type: boolean + additionalProperties: + type: object + additionalProperties: + type: object + did: + type: string + key: + $ref: "#/components/schemas/KeyDescriptor" + participantId: + type: string + roles: + type: array + items: + type: string + serviceEndpoints: + type: array + items: + $ref: "#/components/schemas/Service" + uniqueItems: true + Service: + type: object + properties: + id: + type: string + serviceEndpoint: + type: string + type: + type: string diff --git a/resources/openapi/yaml/identity-api/verifiable-credentials-api.yaml b/resources/openapi/yaml/identity-api/verifiable-credentials-api.yaml new file mode 100644 index 000000000..3c39e9dee --- /dev/null +++ b/resources/openapi/yaml/identity-api/verifiable-credentials-api.yaml @@ -0,0 +1,523 @@ +openapi: 3.0.1 +info: + description: This is the Identity API for manipulating VerifiableCredentials + title: VerifiableCredentials Identity API + version: "1" +paths: + /v1alpha/credentials: + get: + description: Get all VerifiableCredentials across all Participant Contexts. + Requires elevated access. + operationId: getAllCredentials + parameters: + - in: query + name: offset + schema: + type: integer + format: int32 + default: 0 + - in: query + name: limit + schema: + type: integer + format: int32 + default: 50 + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/VerifiableCredentialResource" + description: The list of VerifiableCredential resources. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The query was malformed or was not understood by the server. + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + tags: + - Verifiable Credentials + /v1alpha/participants/{participantContextId}/credentials: + get: + description: Query VerifiableCredentials by type. + operationId: queryCredentialsByType + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: query + name: type + schema: + type: string + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/VerifiableCredentialResource" + description: The list of VerifiableCredentials. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: The query was malformed or was not understood by the server. + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + tags: + - Verifiable Credentials + post: + description: Adds a new VerifiableCredential into the system. + operationId: addCredential + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/VerifiableCredentialManifest" + responses: + "204": + description: The VerifiableCredential was successfully created. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "409": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Could not create VerifiableCredential, because a VerifiableCredential\ + \ with that ID already exists" + tags: + - Verifiable Credentials + put: + description: Update an existing VerifiableCredential. + operationId: updateCredential + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/VerifiableCredentialManifest" + responses: + "204": + description: The VerifiableCredential was updated successfully. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: VerifiableCredential could not be updated because it does not + exist. + tags: + - Verifiable Credentials + /v1alpha/participants/{participantContextId}/credentials/{credentialId}: + delete: + description: Delete a VerifiableCredential. + operationId: deleteCredential + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: credentialId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + type: string + description: The VerifiableCredential was deleted successfully + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A VerifiableCredential with the given ID does not exist. + tags: + - Verifiable Credentials + get: + description: Finds a VerifiableCredential by ID. + operationId: getCredential + parameters: + - description: Base64-Url encode Participant Context ID + in: path + name: participantContextId + required: true + schema: + type: string + - in: path + name: credentialId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/VerifiableCredentialResource" + description: The VerifiableCredential. + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, or the request could not be processed" + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The request could not be completed, because either the authentication\ + \ was missing or was not valid." + "404": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: A VerifiableCredential with the given ID does not exist. + tags: + - Verifiable Credentials +components: + schemas: + Action: + type: object + properties: + constraint: + $ref: "#/components/schemas/Constraint" + includedIn: + type: string + type: + type: string + ApiErrorDetail: + type: object + properties: + invalidValue: + type: object + message: + type: string + path: + type: string + type: + type: string + Constraint: + type: object + discriminator: + propertyName: edctype + properties: + edctype: + type: string + required: + - edctype + CredentialStatus: + type: object + properties: + additionalProperties: + type: object + additionalProperties: + type: object + id: + type: string + type: + type: string + CredentialSubject: + type: object + properties: + id: + type: string + Duty: + type: object + properties: + action: + $ref: "#/components/schemas/Action" + constraints: + type: array + items: + $ref: "#/components/schemas/Constraint" + Issuer: + type: object + properties: + additionalProperties: + type: object + additionalProperties: + type: object + id: + type: string + Permission: + type: object + properties: + action: + $ref: "#/components/schemas/Action" + constraints: + type: array + items: + $ref: "#/components/schemas/Constraint" + duties: + type: array + items: + $ref: "#/components/schemas/Duty" + Policy: + type: object + properties: + '@type': + type: string + enum: + - SET + - OFFER + - CONTRACT + assignee: + type: string + assigner: + type: string + extensibleProperties: + type: object + additionalProperties: + type: object + inheritsFrom: + type: string + obligations: + type: array + items: + $ref: "#/components/schemas/Duty" + permissions: + type: array + items: + $ref: "#/components/schemas/Permission" + profiles: + type: array + items: + type: string + prohibitions: + type: array + items: + $ref: "#/components/schemas/Prohibition" + target: + type: string + Prohibition: + type: object + properties: + action: + $ref: "#/components/schemas/Action" + constraints: + type: array + items: + $ref: "#/components/schemas/Constraint" + remedies: + type: array + items: + $ref: "#/components/schemas/Duty" + VerifiableCredential: + type: object + properties: + credentialStatus: + type: array + items: + $ref: "#/components/schemas/CredentialStatus" + credentialSubject: + type: array + items: + $ref: "#/components/schemas/CredentialSubject" + dataModelVersion: + type: string + enum: + - V_1_1 + - V_2_0 + description: + type: string + expirationDate: + type: string + format: date-time + id: + type: string + issuanceDate: + type: string + format: date-time + issuer: + $ref: "#/components/schemas/Issuer" + name: + type: string + type: + type: array + items: + type: string + VerifiableCredentialContainer: + type: object + properties: + credential: + $ref: "#/components/schemas/VerifiableCredential" + format: + type: string + enum: + - JSON_LD + - JWT + - VC1_0_LD + - VC1_0_JWT + - VC2_0_JOSE + - VC2_0_SD_JWT + - VC2_0_COSE + rawVc: + type: string + VerifiableCredentialManifest: + type: object + properties: + id: + type: string + issuancePolicy: + $ref: "#/components/schemas/Policy" + participantContextId: + type: string + reissuancePolicy: + $ref: "#/components/schemas/Policy" + verifiableCredentialContainer: + $ref: "#/components/schemas/VerifiableCredentialContainer" + VerifiableCredentialResource: + type: object + properties: + credentialStatus: + type: string + enum: + - INITIAL + - REQUESTING + - REQUESTED + - ISSUING + - ISSUED + - REVOKED + - SUSPENDED + - EXPIRED + - NOT_YET_VALID + - ERROR + writeOnly: true + holderId: + type: string + id: + type: string + issuancePolicy: + $ref: "#/components/schemas/Policy" + issuerId: + type: string + participantContextId: + type: string + reissuancePolicy: + $ref: "#/components/schemas/Policy" + state: + type: integer + format: int32 + timeOfLastStatusUpdate: + type: string + format: date-time + timestamp: + type: integer + format: int64 + verifiableCredential: + $ref: "#/components/schemas/VerifiableCredentialContainer" diff --git a/resources/openapi/yaml/presentation-api/credential-request-status-api.yaml b/resources/openapi/yaml/presentation-api/credential-request-status-api.yaml new file mode 100644 index 000000000..85f7fe4ec --- /dev/null +++ b/resources/openapi/yaml/presentation-api/credential-request-status-api.yaml @@ -0,0 +1,203 @@ +openapi: 3.0.1 +info: + description: This represents the Presentation API as per DCP specification. It serves + endpoints to query for specific VerifiablePresentations. + title: Presentation API + version: "1" +paths: + /v1/participants/{participantContextId}/presentations/query: + post: + description: "Issues a new presentation query, that contains either a DIF presentation\ + \ definition, or a list of scopes" + operationId: queryPresentation + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + - in: header + name: Authorization + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PresentationQueryMessage" + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/PresentationResponseMessage" + description: "The query was successfully processed, the response contains\ + \ the VerifiablePresentation" + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, for example when both scope and\ + \ presentationDefinition are given" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: No Authorization header was given. + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The given authentication token could not be validated. This\ + \ can happen, when the request body calls for a broader query scope than\ + \ the granted scope in the auth token" + "501": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "When the request contained a presentationDefinition object,\ + \ but the implementation does not support it." + tags: + - Presentation API +components: + schemas: + ApiErrorDetail: + type: object + example: + message: error message + type: ErrorType + path: object.error.path + invalidValue: this value is not valid + properties: + invalidValue: + type: string + message: + type: string + path: + type: string + type: + type: string + JsonObject: + type: object + additionalProperties: + $ref: "#/components/schemas/JsonValue" + properties: + empty: + type: boolean + valueType: + type: string + enum: + - ARRAY + - OBJECT + - STRING + - NUMBER + - "TRUE" + - "FALSE" + - "NULL" + JsonValue: + type: object + properties: + valueType: + type: string + enum: + - ARRAY + - OBJECT + - STRING + - NUMBER + - "TRUE" + - "FALSE" + - "NULL" + PresentationDefinitionSchema: + type: object + example: | + { + "comment": "taken from https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-definition" + "presentationDefinition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "A specific type of VC", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + PresentationQueryMessage: + type: object + example: + '@context': + - https://w3id.org/tractusx-trust/v0.8 + - https://identity.foundation/presentation-exchange/submission/v1 + '@type': PresentationQueryMessage + presentationDefinition: null + scope: + - org.eclipse.edc.vc.type:SomeCredential_0.3.5:write + - org.eclipse.edc.vc.type:SomeOtherCredential:read + - org.eclipse.edc.vc.type:ThirdCredential:* + properties: + '@context': + type: object + '@type': + type: string + presentationDefinition: + $ref: "#/components/schemas/PresentationDefinitionSchema" + scope: + type: array + items: + type: string + required: + - '@context' + - '@type' + PresentationResponseMessage: + type: object + example: + '@context': + - https://w3id.org/tractusx-trust/v0.8 + '@type': PresentationResponseMessage + presentation: + - dsJdh...UMetV + properties: + '@context': + type: object + presentation: + type: array + items: + type: object + anyOf: + - type: string + - $ref: "#/components/schemas/JsonObject" + required: + - '@context' + - presentation + securitySchemes: + Authentication: + bearerFormat: JWT + description: Self-Issued ID token containing an access_token + scheme: bearer + type: http diff --git a/resources/openapi/yaml/presentation-api/issuer-metadata-api.yaml b/resources/openapi/yaml/presentation-api/issuer-metadata-api.yaml new file mode 100644 index 000000000..85f7fe4ec --- /dev/null +++ b/resources/openapi/yaml/presentation-api/issuer-metadata-api.yaml @@ -0,0 +1,203 @@ +openapi: 3.0.1 +info: + description: This represents the Presentation API as per DCP specification. It serves + endpoints to query for specific VerifiablePresentations. + title: Presentation API + version: "1" +paths: + /v1/participants/{participantContextId}/presentations/query: + post: + description: "Issues a new presentation query, that contains either a DIF presentation\ + \ definition, or a list of scopes" + operationId: queryPresentation + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + - in: header + name: Authorization + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PresentationQueryMessage" + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/PresentationResponseMessage" + description: "The query was successfully processed, the response contains\ + \ the VerifiablePresentation" + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, for example when both scope and\ + \ presentationDefinition are given" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: No Authorization header was given. + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The given authentication token could not be validated. This\ + \ can happen, when the request body calls for a broader query scope than\ + \ the granted scope in the auth token" + "501": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "When the request contained a presentationDefinition object,\ + \ but the implementation does not support it." + tags: + - Presentation API +components: + schemas: + ApiErrorDetail: + type: object + example: + message: error message + type: ErrorType + path: object.error.path + invalidValue: this value is not valid + properties: + invalidValue: + type: string + message: + type: string + path: + type: string + type: + type: string + JsonObject: + type: object + additionalProperties: + $ref: "#/components/schemas/JsonValue" + properties: + empty: + type: boolean + valueType: + type: string + enum: + - ARRAY + - OBJECT + - STRING + - NUMBER + - "TRUE" + - "FALSE" + - "NULL" + JsonValue: + type: object + properties: + valueType: + type: string + enum: + - ARRAY + - OBJECT + - STRING + - NUMBER + - "TRUE" + - "FALSE" + - "NULL" + PresentationDefinitionSchema: + type: object + example: | + { + "comment": "taken from https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-definition" + "presentationDefinition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "A specific type of VC", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + PresentationQueryMessage: + type: object + example: + '@context': + - https://w3id.org/tractusx-trust/v0.8 + - https://identity.foundation/presentation-exchange/submission/v1 + '@type': PresentationQueryMessage + presentationDefinition: null + scope: + - org.eclipse.edc.vc.type:SomeCredential_0.3.5:write + - org.eclipse.edc.vc.type:SomeOtherCredential:read + - org.eclipse.edc.vc.type:ThirdCredential:* + properties: + '@context': + type: object + '@type': + type: string + presentationDefinition: + $ref: "#/components/schemas/PresentationDefinitionSchema" + scope: + type: array + items: + type: string + required: + - '@context' + - '@type' + PresentationResponseMessage: + type: object + example: + '@context': + - https://w3id.org/tractusx-trust/v0.8 + '@type': PresentationResponseMessage + presentation: + - dsJdh...UMetV + properties: + '@context': + type: object + presentation: + type: array + items: + type: object + anyOf: + - type: string + - $ref: "#/components/schemas/JsonObject" + required: + - '@context' + - presentation + securitySchemes: + Authentication: + bearerFormat: JWT + description: Self-Issued ID token containing an access_token + scheme: bearer + type: http diff --git a/resources/openapi/yaml/presentation-api/presentation-api.yaml b/resources/openapi/yaml/presentation-api/presentation-api.yaml new file mode 100644 index 000000000..85f7fe4ec --- /dev/null +++ b/resources/openapi/yaml/presentation-api/presentation-api.yaml @@ -0,0 +1,203 @@ +openapi: 3.0.1 +info: + description: This represents the Presentation API as per DCP specification. It serves + endpoints to query for specific VerifiablePresentations. + title: Presentation API + version: "1" +paths: + /v1/participants/{participantContextId}/presentations/query: + post: + description: "Issues a new presentation query, that contains either a DIF presentation\ + \ definition, or a list of scopes" + operationId: queryPresentation + parameters: + - in: path + name: participantContextId + required: true + schema: + type: string + - in: header + name: Authorization + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PresentationQueryMessage" + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/PresentationResponseMessage" + description: "The query was successfully processed, the response contains\ + \ the VerifiablePresentation" + "400": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "Request body was malformed, for example when both scope and\ + \ presentationDefinition are given" + "401": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: No Authorization header was given. + "403": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "The given authentication token could not be validated. This\ + \ can happen, when the request body calls for a broader query scope than\ + \ the granted scope in the auth token" + "501": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ApiErrorDetail" + description: "When the request contained a presentationDefinition object,\ + \ but the implementation does not support it." + tags: + - Presentation API +components: + schemas: + ApiErrorDetail: + type: object + example: + message: error message + type: ErrorType + path: object.error.path + invalidValue: this value is not valid + properties: + invalidValue: + type: string + message: + type: string + path: + type: string + type: + type: string + JsonObject: + type: object + additionalProperties: + $ref: "#/components/schemas/JsonValue" + properties: + empty: + type: boolean + valueType: + type: string + enum: + - ARRAY + - OBJECT + - STRING + - NUMBER + - "TRUE" + - "FALSE" + - "NULL" + JsonValue: + type: object + properties: + valueType: + type: string + enum: + - ARRAY + - OBJECT + - STRING + - NUMBER + - "TRUE" + - "FALSE" + - "NULL" + PresentationDefinitionSchema: + type: object + example: | + { + "comment": "taken from https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-definition" + "presentationDefinition": { + "id": "first simple example", + "input_descriptors": [ + { + "id": "A specific type of VC", + "name": "A specific type of VC", + "purpose": "We want a VC of this type", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "pattern": "" + } + } + ] + } + } + ] + } + } + PresentationQueryMessage: + type: object + example: + '@context': + - https://w3id.org/tractusx-trust/v0.8 + - https://identity.foundation/presentation-exchange/submission/v1 + '@type': PresentationQueryMessage + presentationDefinition: null + scope: + - org.eclipse.edc.vc.type:SomeCredential_0.3.5:write + - org.eclipse.edc.vc.type:SomeOtherCredential:read + - org.eclipse.edc.vc.type:ThirdCredential:* + properties: + '@context': + type: object + '@type': + type: string + presentationDefinition: + $ref: "#/components/schemas/PresentationDefinitionSchema" + scope: + type: array + items: + type: string + required: + - '@context' + - '@type' + PresentationResponseMessage: + type: object + example: + '@context': + - https://w3id.org/tractusx-trust/v0.8 + '@type': PresentationResponseMessage + presentation: + - dsJdh...UMetV + properties: + '@context': + type: object + presentation: + type: array + items: + type: object + anyOf: + - type: string + - $ref: "#/components/schemas/JsonObject" + required: + - '@context' + - presentation + securitySchemes: + Authentication: + bearerFormat: JWT + description: Self-Issued ID token containing an access_token + scheme: bearer + type: http diff --git a/settings.gradle.kts b/settings.gradle.kts index 6bbe78816..ba5281b73 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -42,7 +42,6 @@ include(":core:lib:keypair-lib") include(":core:lib:accesstoken-lib") // extension modules -include(":extensions:protocols:dcp:presentation-api") include(":extensions:store:sql:identity-hub-did-store-sql") include(":extensions:store:sql:identity-hub-credentials-store-sql") include(":extensions:store:sql:identity-hub-participantcontext-store-sql") @@ -53,6 +52,12 @@ include(":extensions:sts:sts-account-provisioner") include(":extensions:sts:sts-account-service-local") include(":extensions:sts:sts-account-service-remote") +// DCP protocol modules +include(":extensions:protocols:dcp:presentation-api") +include(":extensions:protocols:dcp:credential-request-api") +include(":extensions:protocols:dcp:issuer-metadata-api") +include(":extensions:protocols:dcp:credential-request-status-api") + // Identity APIs include(":extensions:api:identity-api:validators") include(":extensions:api:identity-api:api-configuration") @@ -84,4 +89,7 @@ include(":dist:bom:identityhub-base-bom") include(":dist:bom:identityhub-bom") include(":dist:bom:identityhub-with-sts-bom") include(":dist:bom:identityhub-feature-sql-bom") -include(":dist:bom:identityhub-feature-sql-sts-bom") \ No newline at end of file +include(":dist:bom:identityhub-feature-sql-sts-bom") +include(":dist:bom:issuerservice-base-bom") +include(":dist:bom:issuerservice-bom") +include(":dist:bom:issuerservice-feature-sql-bom") \ No newline at end of file diff --git a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/webcontext/IdentityHubApiContext.java b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/webcontext/IdentityHubApiContext.java index 14af86fb8..a6992b0ee 100644 --- a/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/webcontext/IdentityHubApiContext.java +++ b/spi/identity-hub-spi/src/main/java/org/eclipse/edc/identityhub/spi/webcontext/IdentityHubApiContext.java @@ -18,6 +18,7 @@ public interface IdentityHubApiContext { String IDENTITY = "identity"; String IH_DID = "did"; String PRESENTATION = "presentation"; + String CREDENTIAL_REQUEST = "credential-request"; @Deprecated(since = "0.9.0") String RESOLUTION = "resolution"; }