diff --git a/.mega-linter.yml b/.mega-linter.yml index 1011db191d..8bdeef5477 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -18,11 +18,19 @@ DISABLE_LINTERS: - CPP_CPPLINT # For pollux/lib/anoncreds/src/main/c - JAVA_CHECKSTYLE # For pollux/lib/anoncreds/src/main/java - GHERKIN_GHERKIN_LINT + - OPENAPI_SPECTRAL + # For python, disable all except PYTHON_BLACK linter + - PYTHON_PYLINT + - PYTHON_FLAKE8 + - PYTHON_ISORT + - PYTHON_BANDIT + - PYTHON_MYPY + - PYTHON_PYRIGHT + - PYTHON_RUFF DISABLE_ERRORS_LINTERS: - KOTLIN_KTLINT - PROTOBUF_PROTOLINT - - OPENAPI_SPECTRAL - MARKDOWN_MARKDOWN_LINK_CHECK DISABLE: [COPYPASTE, SPELL, CREDENTIALS] @@ -45,10 +53,12 @@ PRE_COMMANDS: cwd: "workspace" # Linter customisation -MARKDOWN_MARKDOWN_LINK_CHECK_FILTER_REGEX_EXCLUDE: "CHANGELOG.md" -MARKDOWN_MARKDOWNLINT_FILTER_REGEX_EXCLUDE: "CHANGELOG.md" +MARKDOWN_MARKDOWN_LINK_CHECK_FILTER_REGEX_EXCLUDE: CHANGELOG\.md|DEPENDENCIES\.md +MARKDOWN_MARKDOWNLINT_FILTER_REGEX_EXCLUDE: CHANGELOG\.md|DEPENDENCIES\.md +MARKDOWN_MARKDOWN_TABLE_FORMATTER_FILTER_REGEX_EXCLUDE: CHANGELOG\.md|DEPENDENCIES\.md SQL_SQL_LINT_ARGUMENTS: -d postgres --ignore-errors=postgres-invalid-alter-option,postgres-invalid-create-option,postgres-invalid-drop-option -YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*|cloud-agent/service/api/http/*" -YAML_PRETTIER_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*|cloud-agent/service/api/http/*" +YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*|cloud-agent/service/api/http/*|examples/*" +YAML_PRETTIER_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*|cloud-agent/service/api/http/*|examples/*" YAML_V8R_FILTER_REGEX_EXCLUDE: "infrastructure/charts/agent/*" JAVASCRIPT_STANDARD_FILTER_REGEX_EXCLUDE: "tests/performance-tests/agent-performance-tests-k6/src/k6chaijs.js" +BASH_SHELLCHECK_FILTER_REGEX_EXCLUDE: "infrastructure/*" diff --git a/build.sbt b/build.sbt index b004c4338b..c722090209 100644 --- a/build.sbt +++ b/build.sbt @@ -869,6 +869,7 @@ lazy val cloudAgentServer = project eventNotification ) .dependsOn(sharedTest % "test->test") + .dependsOn(polluxCore % "compile->compile;test->test") // ############################ // #### Release process ##### diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/CloudAgentApp.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/CloudAgentApp.scala index 21fcac5bcd..4d1babda28 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/CloudAgentApp.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/CloudAgentApp.scala @@ -19,6 +19,7 @@ import org.hyperledger.identus.iam.entity.http.EntityServerEndpoints import org.hyperledger.identus.iam.wallet.http.WalletManagementServerEndpoints import org.hyperledger.identus.issue.controller.IssueServerEndpoints import org.hyperledger.identus.mercury.{DidOps, HttpClient} +import org.hyperledger.identus.oid4vci.CredentialIssuerServerEndpoints import org.hyperledger.identus.pollux.core.service.{CredentialService, PresentationService} import org.hyperledger.identus.pollux.credentialdefinition.CredentialDefinitionRegistryServerEndpoints import org.hyperledger.identus.pollux.credentialschema.{ @@ -135,6 +136,7 @@ object AgentHttpServer { allEntityEndpoints <- EntityServerEndpoints.all allWalletManagementEndpoints <- WalletManagementServerEndpoints.all allEventEndpoints <- EventServerEndpoints.all + allOIDCEndpoints <- CredentialIssuerServerEndpoints.all } yield allCredentialDefinitionRegistryEndpoints ++ allSchemaRegistryEndpoints ++ allVerificationPolicyEndpoints ++ @@ -148,7 +150,8 @@ object AgentHttpServer { allSystemEndpoints ++ allEntityEndpoints ++ allWalletManagementEndpoints ++ - allEventEndpoints + allEventEndpoints ++ + allOIDCEndpoints def run = for { allEndpoints <- agentRESTServiceEndpoints diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala index fd361473e3..f40513be8d 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/MainApp.scala @@ -29,12 +29,17 @@ import org.hyperledger.identus.event.controller.EventControllerImpl import org.hyperledger.identus.event.notification.EventNotificationServiceImpl import org.hyperledger.identus.iam.authentication.apikey.JdbcAuthenticationRepository import org.hyperledger.identus.iam.authentication.DefaultAuthenticator +import org.hyperledger.identus.iam.authentication.{DefaultAuthenticator, Oid4vciAuthenticatorFactory} +import org.hyperledger.identus.iam.authentication.apikey.JdbcAuthenticationRepository import org.hyperledger.identus.iam.authorization.core.EntityPermissionManagementService import org.hyperledger.identus.iam.authorization.DefaultPermissionManagementService import org.hyperledger.identus.iam.entity.http.controller.{EntityController, EntityControllerImpl} import org.hyperledger.identus.iam.wallet.http.controller.WalletManagementControllerImpl import org.hyperledger.identus.issue.controller.IssueControllerImpl import org.hyperledger.identus.mercury.* +import org.hyperledger.identus.oid4vci.controller.CredentialIssuerControllerImpl +import org.hyperledger.identus.oid4vci.service.OIDCCredentialIssuerServiceImpl +import org.hyperledger.identus.oid4vci.storage.InMemoryIssuanceSessionService import org.hyperledger.identus.pollux.core.service.* import org.hyperledger.identus.pollux.core.service.verification.VcVerificationServiceImpl import org.hyperledger.identus.pollux.credentialdefinition.controller.CredentialDefinitionControllerImpl @@ -48,6 +53,7 @@ import org.hyperledger.identus.pollux.sql.repository.{ JdbcCredentialRepository, JdbcCredentialSchemaRepository, JdbcCredentialStatusListRepository, + JdbcOID4VCIIssuerMetadataRepository, JdbcPresentationRepository, JdbcVerificationPolicyRepository, Migrations as PolluxMigrations @@ -191,6 +197,7 @@ object MainApp extends ZIOAppDefault { DefaultAuthenticator.layer, DefaultPermissionManagementService.layer, EntityPermissionManagementService.layer, + Oid4vciAuthenticatorFactory.layer, // grpc GrpcModule.prismNodeStubLayer, // storage @@ -205,7 +212,13 @@ object MainApp extends ZIOAppDefault { RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcCredentialSchemaRepository.layer, RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcCredentialDefinitionRepository.layer, RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcPresentationRepository.layer, + RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcOID4VCIIssuerMetadataRepository.layer, RepoModule.polluxContextAwareTransactorLayer >>> JdbcVerificationPolicyRepository.layer, + // oidc + CredentialIssuerControllerImpl.layer, + InMemoryIssuanceSessionService.layer, + OID4VCIIssuerMetadataServiceImpl.layer, + OIDCCredentialIssuerServiceImpl.layer, // event notification service ZLayer.succeed(500) >>> EventNotificationServiceImpl.layer, // HTTP client diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobsHelper.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobsHelper.scala index 7aed15be45..6e09b1f084 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobsHelper.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/jobs/BackgroundJobsHelper.scala @@ -6,6 +6,12 @@ import org.hyperledger.identus.agent.walletapi.service.ManagedDIDService import org.hyperledger.identus.agent.walletapi.storage.DIDNonSecretStorage import org.hyperledger.identus.castor.core.model.did.{LongFormPrismDID, PrismDID, VerificationRelationship} import org.hyperledger.identus.castor.core.model.did.EllipticCurve +import org.hyperledger.identus.castor.core.model.did.{ + EllipticCurve, + LongFormPrismDID, + PrismDID, + VerificationRelationship +} import org.hyperledger.identus.castor.core.service.DIDService import org.hyperledger.identus.mercury.{AgentPeerService, DidAgent} import org.hyperledger.identus.mercury.model.DidId diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/EndpointOutputs.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/EndpointOutputs.scala index 819ce88451..5b5dc599c2 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/EndpointOutputs.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/EndpointOutputs.scala @@ -6,7 +6,7 @@ import sttp.tapir.json.zio.jsonBody import sttp.tapir.EndpointOutput.OneOfVariant object EndpointOutputs { - private def statusCodeMatcher( + def statusCodeMatcher( statusCode: StatusCode ): PartialFunction[Any, Boolean] = { case ErrorResponse(status, _, _, _, _) if status == statusCode.code => true diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Authenticator.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Authenticator.scala index deb81bb6a9..02bfa8375f 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Authenticator.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Authenticator.scala @@ -51,8 +51,8 @@ trait Authorizer[E <: BaseEntity] { .mapError(msg => AuthenticationError.UnexpectedError(s"Unable to retrieve entity role for entity id ${entity.id}. $msg") ) - .filterOrFail(_ != EntityRole.Admin)( - AuthenticationError.InvalidRole("Admin role is not allowed to access the tenant's wallet.") + .filterOrFail(_ == EntityRole.Tenant)( + AuthenticationError.InvalidRole("Only Tenant role is allowed to access the tenant's wallet.") ) .flatMap(_ => authorizeWalletAccessLogic(entity)) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Oid4vciAuthenticator.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Oid4vciAuthenticator.scala new file mode 100644 index 0000000000..719e94b440 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/Oid4vciAuthenticator.scala @@ -0,0 +1,90 @@ +package org.hyperledger.identus.iam.authentication + +import org.hyperledger.identus.agent.walletapi.model.{BaseEntity, EntityRole} +import org.hyperledger.identus.iam.authentication.oidc.{ + AccessToken, + JwtAuthenticationError, + JwtCredentials, + Oauth2TokenIntrospector, + RemoteOauth2TokenIntrospector +} +import org.hyperledger.identus.oid4vci.service.OIDCCredentialIssuerService +import org.hyperledger.identus.pollux.core.service.OID4VCIIssuerMetadataService +import zio.* +import zio.http.Client + +import java.util.UUID + +final case class ExternalEntity(id: UUID) extends BaseEntity { + override def role: Either[String, EntityRole] = Right(EntityRole.ExternalParty) +} + +case class Oid4vciAuthenticator(tokenIntrospector: Oauth2TokenIntrospector) extends Authenticator[ExternalEntity] { + + override def isEnabled: Boolean = true + + def authenticate(credentials: Credentials): IO[AuthenticationError, ExternalEntity] = { + credentials match { + case JwtCredentials(Some(token)) if token.nonEmpty => authenticate(token) + case JwtCredentials(Some(_)) => ZIO.fail(JwtAuthenticationError.emptyToken) + case JwtCredentials(None) => ZIO.fail(AuthenticationError.InvalidCredentials("Bearer token is not provided")) + case other => ZIO.fail(AuthenticationError.InvalidCredentials("Bearer token is not provided")) + } + } + + private def authenticate(token: String): IO[AuthenticationError, ExternalEntity] = { + for { + accessToken <- ZIO + .fromEither(AccessToken.fromString(token)) + .mapError(AuthenticationError.InvalidCredentials.apply) + introspection <- tokenIntrospector + .introspectToken(accessToken) + .mapError(e => AuthenticationError.UnexpectedError(e.getMessage)) + _ <- ZIO + .fail(AuthenticationError.InvalidCredentials("The accessToken is invalid.")) + .unless(introspection.active) + entityId <- ZIO + .fromOption(introspection.sub) + .mapError(_ => AuthenticationError.UnexpectedError("Subject ID is not found in the accessToken.")) + .flatMap { id => + ZIO + .attempt(UUID.fromString(id)) + .mapError(e => AuthenticationError.UnexpectedError(s"Subject ID in accessToken is not a UUID. $e")) + } + } yield ExternalEntity(entityId) + } +} + +class Oid4vciAuthenticatorFactory( + httpClient: Client, + issuerService: OIDCCredentialIssuerService, + metadataService: OID4VCIIssuerMetadataService +) { + def make(issuerState: String): IO[AuthenticationError, Oid4vciAuthenticator] = + issuerService + .getIssuanceSessionByIssuerState(issuerState) + .mapError(e => + AuthenticationError.UnexpectedError(s"Unable to get issuanceSession from issuerState: $issuerState") + ) + .flatMap(session => make(session.issuerId)) + + def make(issuerId: UUID): IO[AuthenticationError, Oid4vciAuthenticator] = + for { + issuer <- metadataService + .getCredentialIssuer(issuerId) + .mapError(e => AuthenticationError.UnexpectedError(s"Unable to get issuer from issuerId: $issuerId")) + tokenIntrospector <- RemoteOauth2TokenIntrospector + .fromAuthorizationServer( + httpClient, + issuer.authorizationServer, + issuer.authorizationServerClientId, + issuer.authorizationServerClientSecret + ) + .mapError(e => AuthenticationError.UnexpectedError(s"Unable to create token introspector: $e")) + } yield Oid4vciAuthenticator(tokenIntrospector) +} + +object Oid4vciAuthenticatorFactory { + def layer: URLayer[Client & OIDCCredentialIssuerService & OID4VCIIssuerMetadataService, Oid4vciAuthenticatorFactory] = + ZLayer.fromFunction(Oid4vciAuthenticatorFactory(_, _, _)) +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticator.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticator.scala index bf850df93e..146253739a 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticator.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticator.scala @@ -60,7 +60,7 @@ final class AccessToken private (token: String, claims: JwtClaim, rolesClaimPath } object AccessToken { - def fromString(token: String, rolesClaimPath: Seq[String]): Either[String, AccessToken] = + def fromString(token: String, rolesClaimPath: Seq[String] = Nil): Either[String, AccessToken] = JwtCirce .decode(token, JwtOptions(false, false, false)) .map(claims => AccessToken(token, claims, rolesClaimPath)) diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticatorImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticatorImpl.scala index dc271e8b4b..5c416a4d19 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticatorImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakAuthenticatorImpl.scala @@ -81,6 +81,8 @@ class KeycloakAuthenticatorImpl( ctx <- role match { case EntityRole.Admin => ZIO.succeed(WalletAdministrationContext.Admin()) case EntityRole.Tenant => selfServiceCtx + case EntityRole.ExternalParty => + ZIO.fail(AuthenticationError.InvalidRole("External party cannot access the wallet.")) } } yield ctx } diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakClient.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakClient.scala index 935531d25f..9b448207c0 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakClient.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/KeycloakClient.scala @@ -6,17 +6,8 @@ import zio.* import zio.http.* import zio.json.* -import java.net.URLEncoder -import java.nio.charset.StandardCharsets import scala.jdk.CollectionConverters.* -final case class TokenIntrospection(active: Boolean, sub: Option[String]) - -object TokenIntrospection { - given JsonEncoder[TokenIntrospection] = JsonEncoder.derived - given JsonDecoder[TokenIntrospection] = JsonDecoder.derived -} - final case class TokenResponse(access_token: String, refresh_token: String) object TokenResponse { @@ -50,51 +41,19 @@ trait KeycloakClient { class KeycloakClientImpl(client: AuthzClient, httpClient: Client, override val keycloakConfig: KeycloakConfig) extends KeycloakClient { - private val introspectionUrl = client.getServerConfiguration().getIntrospectionEndpoint() + private val introspector: Oauth2TokenIntrospector = RemoteOauth2TokenIntrospector( + client.getServerConfiguration().getIntrospectionEndpoint(), + httpClient, + keycloakConfig.clientId, + keycloakConfig.clientSecret + ) private val tokenUrl = client.getServerConfiguration().getTokenEndpoint() private val baseFormHeaders = Headers(Header.ContentType(MediaType.application.`x-www-form-urlencoded`)) - // TODO: support offline introspection // https://www.keycloak.org/docs/22.0.4/securing_apps/#_token_introspection_endpoint - override def introspectToken(token: AccessToken): IO[KeycloakClientError, TokenIntrospection] = { - (for { - url <- ZIO.fromEither(URL.decode(introspectionUrl)).orDie - response <- httpClient - .request( - Request( - url = url, - method = Method.POST, - headers = baseFormHeaders ++ Headers( - Header.Authorization.Basic( - username = URLEncoder.encode(keycloakConfig.clientId, StandardCharsets.UTF_8), - password = URLEncoder.encode(keycloakConfig.clientSecret, StandardCharsets.UTF_8) - ) - ), - body = Body.fromURLEncodedForm( - Form( - FormField.simpleField("token", token.toString) - ) - ) - ) - ) - .logError("Fail to introspect token on keycloak.") - .mapError(e => KeycloakClientError.UnexpectedError("Fail to introspect the token on keycloak.")) - body <- response.body.asString - .logError("Fail parse keycloak introspection response.") - .mapError(e => KeycloakClientError.UnexpectedError("Fail parse keycloak introspection response.")) - result <- - if (response.status.code == 200) { - ZIO - .fromEither(body.fromJson[TokenIntrospection]) - .logError("Fail to decode keycloak token introspection response") - .mapError(e => KeycloakClientError.UnexpectedError(e)) - } else { - ZIO.logError(s"Keycloak token introspection was unsucessful. Status: ${response.status}. Response: $body") *> - ZIO.fail(KeycloakClientError.UnexpectedError("Token introspection was unsuccessful.")) - } - } yield result).provide(Scope.default) - } + override def introspectToken(token: AccessToken): IO[KeycloakClientError, TokenIntrospection] = + introspector.introspectToken(token).mapError(e => KeycloakClientError.UnexpectedError(e.getMessage)) override def getAccessToken(username: String, password: String): IO[KeycloakClientError, TokenResponse] = { (for { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/Oauth2TokenIntrospector.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/Oauth2TokenIntrospector.scala new file mode 100644 index 0000000000..2bfa0b9e20 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/iam/authentication/oidc/Oauth2TokenIntrospector.scala @@ -0,0 +1,95 @@ +package org.hyperledger.identus.iam.authentication.oidc + +import zio.* +import zio.http.* +import zio.json.* + +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +final case class AuthorizationServerMetadata(introspection_endpoint: String) + +object AuthorizationServerMetadata { + given JsonEncoder[AuthorizationServerMetadata] = JsonEncoder.derived + given JsonDecoder[AuthorizationServerMetadata] = JsonDecoder.derived +} + +final case class TokenIntrospection(active: Boolean, sub: Option[String]) + +object TokenIntrospection { + given JsonEncoder[TokenIntrospection] = JsonEncoder.derived + given JsonDecoder[TokenIntrospection] = JsonDecoder.derived +} + +trait Oauth2TokenIntrospector { + def introspectToken(token: AccessToken): IO[Throwable, TokenIntrospection] +} + +// TODO: support offline introspection +class RemoteOauth2TokenIntrospector( + introspectionUrl: String, + httpClient: Client, + clientId: String, + clientSecret: String +) extends Oauth2TokenIntrospector { + + private val baseFormHeaders = Headers(Header.ContentType(MediaType.application.`x-www-form-urlencoded`)) + + // https://www.keycloak.org/docs/22.0.4/securing_apps/#_token_introspection_endpoint + override def introspectToken(token: AccessToken): Task[TokenIntrospection] = { + (for { + url <- ZIO.fromEither(URL.decode(introspectionUrl)).orDie + response <- httpClient + .request( + Request( + url = url, + method = Method.POST, + headers = baseFormHeaders ++ Headers( + Header.Authorization.Basic( + username = URLEncoder.encode(clientId, StandardCharsets.UTF_8), + password = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + ) + ), + body = Body.fromURLEncodedForm( + Form( + FormField.simpleField("token", token.toString) + ) + ) + ) + ) + .logError("Fail to introspect token on keycloak.") + body <- response.body.asString + .logError("Fail parse keycloak introspection response.") + result <- + if (response.status.code == 200) { + ZIO + .fromEither(body.fromJson[TokenIntrospection]) + .logError("Fail to decode keycloak token introspection response") + .mapError(RuntimeException(_)) + } else { + ZIO.logError(s"Keycloak token introspection was unsucessful. Status: ${response.status}. Response: $body") *> + ZIO.fail(RuntimeException("Token introspection did not return a successful result.")) + } + } yield result).provide(Scope.default) + } + +} + +object RemoteOauth2TokenIntrospector { + def fromAuthorizationServer( + httpClient: Client, + authorizationServer: java.net.URL, + clientId: String, + clientSecret: String + ): Task[RemoteOauth2TokenIntrospector] = { + for { + url <- ZIO.fromEither(URL.decode(authorizationServer.toString())).orDie + metadataUrl = url / ".well-known" / "openid-configuration" + response <- httpClient.request(Request(url = metadataUrl, method = Method.GET)) + body <- response.body.asString + metadata <- ZIO + .fromEither(body.fromJson[AuthorizationServerMetadata]) + .mapError(e => RuntimeException(s"Unable to parse authorization server metadata: $e")) + } yield RemoteOauth2TokenIntrospector(metadata.introspection_endpoint, httpClient, clientId, clientSecret) + }.provide(Scope.default) +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/CredentialIssuerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/CredentialIssuerEndpoints.scala new file mode 100644 index 0000000000..c9fc7b0572 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/CredentialIssuerEndpoints.scala @@ -0,0 +1,256 @@ +package org.hyperledger.identus.oid4vci + +import org.hyperledger.identus.api.http.{EndpointOutputs, ErrorResponse, RequestContext} +import org.hyperledger.identus.iam.authentication.apikey.ApiKeyCredentials +import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityLogic.apiKeyHeader +import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials +import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader +import org.hyperledger.identus.oid4vci.http.* +import org.hyperledger.identus.oid4vci.http.ExtendedErrorResponse.given +import sttp.apispec.Tag +import sttp.model.StatusCode +import sttp.tapir.* +import sttp.tapir.json.zio.jsonBody + +import java.util.UUID + +object CredentialIssuerEndpoints { + + private val tagName = "OpenID for Verifiable Credential Issuance" + private val tagDescription = + s""" + |The __${tagName}__ is a service that issues credentials to users by implementing the [OIDC for Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) specification. + |It exposes the following endpoints: + |- Credential Endpoint + |- Credential Issuer Metadata Endpoint + |- Credential Offer Endpoint + |""".stripMargin + + val tag = Tag(tagName, Some(tagDescription)) + + private val issuerIdPathSegment = path[UUID]("issuerId") + .description("An issuer identifier in the oid4vci protocol") + .example(UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479")) + + private val credentialConfigIdSegment = path[String]("credentialConfigId") + .description("An identifier for the credential configuration") + .example("UniversityDegree") + + private val baseEndpoint = endpoint + .tag(tagName) + .in(extractFromRequest[RequestContext](RequestContext.apply)) + .in("oid4vci") + + private val baseIssuerEndpoint = baseEndpoint.in("issuers") + + private val baseIssuerPrivateEndpoint = baseIssuerEndpoint + .securityIn(apiKeyHeader) + .securityIn(jwtAuthHeader) + + val credentialEndpointErrorOutput = oneOf[ExtendedErrorResponse]( + oneOfVariantValueMatcher(StatusCode.BadRequest, jsonBody[ExtendedErrorResponse]) { + case CredentialErrorResponse(code, _, _, _) if code.toHttpStatusCode == StatusCode.BadRequest => true + }, + oneOfVariantValueMatcher(StatusCode.Unauthorized, jsonBody[ExtendedErrorResponse]) { + case CredentialErrorResponse(code, _, _, _) if code.toHttpStatusCode == StatusCode.Unauthorized => true + }, + oneOfVariantValueMatcher(StatusCode.Forbidden, jsonBody[ExtendedErrorResponse]) { + case CredentialErrorResponse(code, _, _, _) if code.toHttpStatusCode == StatusCode.Forbidden => true + }, + oneOfVariantValueMatcher(StatusCode.InternalServerError, jsonBody[ExtendedErrorResponse]) { + case ErrorResponse(status, _, _, _, _) if status == StatusCode.InternalServerError.code => true + } + ) + + val credentialEndpoint: Endpoint[ + JwtCredentials, + (RequestContext, UUID, CredentialRequest), + ExtendedErrorResponse, + CredentialResponse, + Any + ] = baseIssuerEndpoint.post + .in(issuerIdPathSegment / "credentials") + .in(jsonBody[CredentialRequest]) + .securityIn(jwtAuthHeader) + .out( + statusCode(StatusCode.Ok).description("Credential issued successfully"), + ) + .out(jsonBody[CredentialResponse]) + .errorOut(credentialEndpointErrorOutput) + .name("issueCredential") + .summary("Credential Endpoint") + .description( + """OID for VCI [Credential Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-endpoint)""".stripMargin + ) + + val createCredentialOfferEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID, CredentialOfferRequest), + ErrorResponse, + CredentialOfferResponse, + Any + ] = baseIssuerPrivateEndpoint.post + .in(issuerIdPathSegment / "credential-offers") + .in(jsonBody[CredentialOfferRequest]) + .out( + statusCode(StatusCode.Created).description("CredentialOffer created successfully"), + ) + .out(jsonBody[CredentialOfferResponse]) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("createCredentialOffer") + .summary("Create a new credential offer") + .description( + """Create a new credential offer and return a compliant `CredentialOffer` for the holder's + |[Credential Offer Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer-endpoint).""".stripMargin + ) + + val nonceEndpoint: Endpoint[ + JwtCredentials, + (RequestContext, NonceRequest), + ErrorResponse, + NonceResponse, + Any + ] = baseEndpoint.post + .in("nonces") + .in(jsonBody[NonceRequest]) + .securityIn(jwtAuthHeader) + .out( + statusCode(StatusCode.Ok).description("Nonce issued successfully"), + ) + .out(jsonBody[NonceResponse]) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("getNonce") + .summary("Nonce Endpoint") + .description( + """The endpoint that returns a `nonce` value for the [Token Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-nonce-endpoint)""".stripMargin + ) + + val createCredentialIssuerEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, CreateCredentialIssuerRequest), + ErrorResponse, + CredentialIssuer, + Any + ] = baseIssuerPrivateEndpoint.post + .in(jsonBody[CreateCredentialIssuerRequest]) + .out( + statusCode(StatusCode.Created).description("Credential issuer created successfully") + ) + .out(jsonBody[CredentialIssuer]) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("createCredentialIssuer") + .summary("Create a new credential issuer") + + val getCredentialIssuersEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + RequestContext, + ErrorResponse, + CredentialIssuerPage, + Any + ] = baseIssuerPrivateEndpoint.get + .errorOut(EndpointOutputs.basicFailuresAndForbidden) + .out(statusCode(StatusCode.Ok).description("List the credential issuers")) + .out(jsonBody[CredentialIssuerPage]) + .name("getCredentialIssuers") + .summary("List all credential issuers") + + val updateCredentialIssuerEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID, PatchCredentialIssuerRequest), + ErrorResponse, + CredentialIssuer, + Any + ] = baseIssuerPrivateEndpoint.patch + .in(issuerIdPathSegment) + .in(jsonBody[PatchCredentialIssuerRequest]) + .out( + statusCode(StatusCode.Ok).description("Credential issuer updated successfully") + ) + .out(jsonBody[CredentialIssuer]) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("updateCredentialIssuer") + .summary("Update the credential issuer") + + val deleteCredentialIssuerEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID), + ErrorResponse, + Unit, + Any + ] = baseIssuerPrivateEndpoint.delete + .in(issuerIdPathSegment) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .out(statusCode(StatusCode.Ok).description("Credential issuer deleted successfully")) + .name("deleteCredentialIssuer") + .summary("Delete the credential issuer") + + val createCredentialConfigurationEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID, CreateCredentialConfigurationRequest), + ErrorResponse, + CredentialConfiguration, + Any + ] = baseIssuerPrivateEndpoint.post + .in(issuerIdPathSegment / "credential-configurations") + .in(jsonBody[CreateCredentialConfigurationRequest]) + .out( + statusCode(StatusCode.Created).description("Credential configuration created successfully") + ) + .out(jsonBody[CredentialConfiguration]) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("createCredentialConfiguration") + .summary("Create a new credential configuration") + .description( + """Create a new credential configuration for the issuer. + |It represents the configuration of the credential that can be issued by the issuer. + |This credential configuration object will be displayed in the credential issuer metadata.""".stripMargin + ) + + val getCredentialConfigurationEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID, String), + ErrorResponse, + CredentialConfiguration, + Any + ] = baseIssuerPrivateEndpoint.get + .in(issuerIdPathSegment / "credential-configurations" / credentialConfigIdSegment) + .out( + statusCode(StatusCode.Ok).description("Get credential configuration successfully") + ) + .out(jsonBody[CredentialConfiguration]) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("getCredentialConfiguration") + .summary("Get the credential configuration") + + val deleteCredentialConfigurationEndpoint: Endpoint[ + (ApiKeyCredentials, JwtCredentials), + (RequestContext, UUID, String), + ErrorResponse, + Unit, + Any + ] = baseIssuerPrivateEndpoint.delete + .in(issuerIdPathSegment / "credential-configurations" / credentialConfigIdSegment) + .out( + statusCode(StatusCode.Ok).description("Credential configuration deleted successfully") + ) + .errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden) + .name("deleteCredentialConfiguration") + .summary("Delete the credential configuration") + + val issuerMetadataEndpoint: Endpoint[ + Unit, + (RequestContext, UUID), + ErrorResponse, + IssuerMetadata, + Any + ] = baseIssuerEndpoint.get + .in(issuerIdPathSegment / ".well-known" / "openid-credential-issuer") + .out( + statusCode(StatusCode.Ok).description("Issuer Metadata successfully retrieved") + ) + .out(jsonBody[IssuerMetadata]) + .errorOut(EndpointOutputs.basicFailuresAndNotFound) + .name("getIssuerMetadata") + .summary("Get the credential issuer metadata") + +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/CredentialIssuerServerEndpoints.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/CredentialIssuerServerEndpoints.scala new file mode 100644 index 0000000000..43d522cf30 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/CredentialIssuerServerEndpoints.scala @@ -0,0 +1,194 @@ +package org.hyperledger.identus.oid4vci + +import org.hyperledger.identus.agent.walletapi.model.BaseEntity +import org.hyperledger.identus.api.http.ErrorResponse +import org.hyperledger.identus.iam.authentication.{ + AuthenticationError, + Authenticator, + Authorizer, + DefaultAuthenticator, + Oid4vciAuthenticatorFactory, + SecurityLogic +} +import org.hyperledger.identus.oid4vci.controller.CredentialIssuerController +import org.hyperledger.identus.oid4vci.http.{CredentialErrorResponse, ExtendedErrorResponse, NonceResponse} +import org.hyperledger.identus.LogUtils.* +import sttp.tapir.ztapir.* +import zio.* + +import scala.language.implicitConversions + +case class CredentialIssuerServerEndpoints( + credentialIssuerController: CredentialIssuerController, + authenticator: Authenticator[BaseEntity], + authorizer: Authorizer[BaseEntity], + oid4vciAuthenticatorFactory: Oid4vciAuthenticatorFactory +) { + val credentialServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.credentialEndpoint + .zServerSecurityLogic(ZIO.succeed) + .serverLogic { jwt => + { case (rc, issuerId, request) => + oid4vciAuthenticatorFactory + .make(issuerId) + .flatMap(_.authenticate(jwt)) + .mapError[CredentialErrorResponse](identity) + .flatMap { entity => // FIXME: this entity should be checked + credentialIssuerController + .issueCredential(rc, issuerId, request) + .logTrace(rc) + } + } + } + + val createCredentialOfferServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.createCredentialOfferEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, issuerId, request) => + credentialIssuerController + .createCredentialOffer(rc, issuerId, request) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val nonceServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.nonceEndpoint + .zServerSecurityLogic(ZIO.succeed) + .serverLogic { jwt => + { case (rc, request) => + oid4vciAuthenticatorFactory + .make(request.issuerState) + .flatMap(_.authenticate(jwt)) + .mapError(AuthenticationError.toErrorResponse) + .flatMap { entity => + credentialIssuerController + .getNonce(rc, request) + .logTrace(rc) + } + } + } + + val createCredentialIssuerServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.createCredentialIssuerEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, request) => + credentialIssuerController + .createCredentialIssuer(rc, request) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val getCredentialIssuersServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.getCredentialIssuersEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case rc => + credentialIssuerController + .getCredentialIssuers(rc) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val updateCredentialIssuerServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.updateCredentialIssuerEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, issuerId, request) => + credentialIssuerController + .updateCredentialIssuer(rc, issuerId, request) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val deleteCredentialIssuerServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.deleteCredentialIssuerEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, issuerId) => + credentialIssuerController + .deleteCredentialIssuer(rc, issuerId) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val createCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.createCredentialConfigurationEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, issuerId, request) => + credentialIssuerController + .createCredentialConfiguration(rc, issuerId, request) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val getCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.getCredentialConfigurationEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, issuerId, configurationId) => + credentialIssuerController + .getCredentialConfiguration(rc, issuerId, configurationId) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val deleteCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] = + CredentialIssuerEndpoints.deleteCredentialConfigurationEndpoint + .zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer)) + .serverLogic { wac => + { case (rc, issuerId, configurationId) => + credentialIssuerController + .deleteCredentialConfiguration(rc, issuerId, configurationId) + .provideSomeLayer(ZLayer.succeed(wac)) + .logTrace(rc) + } + } + + val issuerMetadataServerEndpoint: ZServerEndpoint[Any, Any] = CredentialIssuerEndpoints.issuerMetadataEndpoint + .zServerLogic { + { case (rc, didRef) => credentialIssuerController.getIssuerMetadata(rc, didRef).logTrace(rc) } + } + + val all: List[ZServerEndpoint[Any, Any]] = List( + credentialServerEndpoint, + createCredentialOfferServerEndpoint, + nonceServerEndpoint, + createCredentialIssuerServerEndpoint, + getCredentialIssuersServerEndpoint, + updateCredentialIssuerServerEndpoint, + deleteCredentialIssuerServerEndpoint, + createCredentialConfigurationServerEndpoint, + getCredentialConfigurationServerEndpoint, + deleteCredentialConfigurationServerEndpoint, + issuerMetadataServerEndpoint + ) +} + +object CredentialIssuerServerEndpoints { + def all: URIO[ + DefaultAuthenticator & Oid4vciAuthenticatorFactory & CredentialIssuerController, + List[ZServerEndpoint[Any, Any]] + ] = { + for { + authenticator <- ZIO.service[DefaultAuthenticator] + credentialIssuerController <- ZIO.service[CredentialIssuerController] + oid4vciAuthenticatorFactory <- ZIO.service[Oid4vciAuthenticatorFactory] + oidcEndpoints = CredentialIssuerServerEndpoints( + credentialIssuerController, + authenticator, + authenticator, + oid4vciAuthenticatorFactory + ) + } yield oidcEndpoints.all + } +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala new file mode 100644 index 0000000000..4fcfc4f315 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/controller/CredentialIssuerController.scala @@ -0,0 +1,349 @@ +package org.hyperledger.identus.oid4vci.controller + +import org.hyperledger.identus.agent.server.config.AppConfig +import org.hyperledger.identus.api.http.{ErrorResponse, RequestContext} +import org.hyperledger.identus.api.http.ErrorResponse.{badRequest, internalServerError} +import org.hyperledger.identus.api.util.PaginationUtils +import org.hyperledger.identus.castor.core.model.did.PrismDID +import org.hyperledger.identus.oid4vci.domain.Openid4VCIProofJwtOps +import org.hyperledger.identus.oid4vci.http.* +import org.hyperledger.identus.oid4vci.http.CredentialErrorCode.* +import org.hyperledger.identus.oid4vci.service.OIDCCredentialIssuerService +import org.hyperledger.identus.pollux.core.model.oid4vci.CredentialIssuer as PolluxCredentialIssuer +import org.hyperledger.identus.pollux.core.service.OID4VCIIssuerMetadataService +import org.hyperledger.identus.pollux.vc.jwt.JWT +import org.hyperledger.identus.shared.models.WalletAccessContext +import zio.{IO, URLayer, ZIO, ZLayer} + +import java.net.{URI, URL} +import java.util.UUID +import scala.language.implicitConversions + +trait CredentialIssuerController { + def issueCredential( + ctx: RequestContext, + issuerId: UUID, + credentialRequest: CredentialRequest + ): IO[ExtendedErrorResponse, CredentialResponse] + + def createCredentialOffer( + ctx: RequestContext, + issuerId: UUID, + credentialOfferRequest: CredentialOfferRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialOfferResponse] + + def getNonce( + ctx: RequestContext, + request: NonceRequest + ): IO[ErrorResponse, NonceResponse] + + def createCredentialIssuer( + ctx: RequestContext, + request: CreateCredentialIssuerRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuer] + + def getCredentialIssuers( + ctx: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuerPage] + + def updateCredentialIssuer( + ctx: RequestContext, + issuerId: UUID, + request: PatchCredentialIssuerRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuer] + + def deleteCredentialIssuer( + ctx: RequestContext, + issuerId: UUID, + ): ZIO[WalletAccessContext, ErrorResponse, Unit] + + def createCredentialConfiguration( + ctx: RequestContext, + issuerId: UUID, + request: CreateCredentialConfigurationRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialConfiguration] + + def getCredentialConfiguration( + ctx: RequestContext, + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, ErrorResponse, CredentialConfiguration] + + def deleteCredentialConfiguration( + ctx: RequestContext, + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, ErrorResponse, Unit] + + def getIssuerMetadata( + ctx: RequestContext, + issuerId: UUID + ): IO[ErrorResponse, IssuerMetadata] +} + +object CredentialIssuerController { + object Errors { + def badRequestInvalidDID(didRef: String, details: String): ExtendedErrorResponse = + CredentialErrorResponse( + error = invalid_request, + errorDescription = Some(s"Invalid DID input: $didRef. Error: $details") + ) + + def badRequestDIDResolutionFailed(didRef: String, details: String): ExtendedErrorResponse = + CredentialErrorResponse( + error = invalid_request, + errorDescription = Some(s"Failed to resolve DID: $didRef. Error: $details") + ) + + def badRequestInvalidProof(jwt: String, details: String): ExtendedErrorResponse = + CredentialErrorResponse(error = invalid_proof, errorDescription = Some(s"Invalid proof: $jwt. Error: $details")) + + def badRequestUnsupportedCredentialFormat(format: CredentialFormat): ExtendedErrorResponse = + CredentialErrorResponse( + error = unsupported_credential_format, + errorDescription = Some(s"Unsupported credential format: $format") + ) + + def badRequestUnsupportedCredentialType(details: String): ExtendedErrorResponse = + CredentialErrorResponse( + error = unsupported_credential_type, + errorDescription = Some(s"Unsupported credential type. Error: $details") + ) + + def serverError(details: Option[String]): ExtendedErrorResponse = + internalServerError("InternalServerError", details) + } +} + +case class CredentialIssuerControllerImpl( + credentialIssuerService: OIDCCredentialIssuerService, + issuerMetadataService: OID4VCIIssuerMetadataService, + agentBaseUrl: URL +) extends CredentialIssuerController + with Openid4VCIProofJwtOps { + + import CredentialIssuerController.Errors.* + import OIDCCredentialIssuerService.Errors.* + + private def parseURL(url: String): IO[ErrorResponse, URL] = + ZIO + .attempt(URI.create(url).toURL()) + .mapError(ue => badRequest(detail = Some(s"Invalid URL: $url"))) + + private def baseCredentialIssuerUrl(issuerId: UUID): URL = + URI(s"$agentBaseUrl/oid4vci/issuers/$issuerId").toURL() + + def issueCredential( + ctx: RequestContext, + issuerId: UUID, + credentialRequest: CredentialRequest + ): IO[ExtendedErrorResponse, CredentialResponse] = { + credentialRequest match + case JwtCredentialRequest( + format, + proof, + credentialIdentifier, + credentialResponseEncryption, + credentialDefinition + ) => + issueJwtCredential(issuerId, proof, credentialIdentifier, credentialDefinition, credentialResponseEncryption) + case other: CredentialRequest => // add other formats here + ZIO.fail(badRequestUnsupportedCredentialFormat(credentialRequest.format)) + } + + def issueJwtCredential( + issuerId: UUID, + maybeProof: Option[Proof], + maybeCredentialIdentifier: Option[String], + maybeCredentialDefinition: Option[CredentialDefinition], + maybeEncryption: Option[CredentialResponseEncryption] + ): IO[ExtendedErrorResponse, CredentialResponse] = { + maybeProof match { + case Some(JwtProof(proofType, jwt)) => + for { + _ <- ZIO + .ifZIO(credentialIssuerService.verifyJwtProof(JWT(jwt)))( + ZIO.unit, + ZIO.fail(OIDCCredentialIssuerService.Errors.InvalidProof("Invalid proof")) + ) + .mapError { case InvalidProof(message) => + badRequestInvalidProof(jwt, message) + } + nonce <- getNonceFromJwt(JWT(jwt)) + .mapError(throwable => badRequestInvalidProof(jwt, throwable.getMessage)) + session <- credentialIssuerService + .getIssuanceSessionByNonce(nonce) + .mapError(_ => badRequestInvalidProof(jwt, "nonce is not associated to the issuance session")) + subjectDid <- getSubjectDIDFromJwt(JWT(jwt)) + .mapError(throwable => badRequestInvalidProof(jwt, throwable.getMessage)) + sessionWithSubjectDid <- credentialIssuerService + .updateIssuanceSession(session.withSubjectDid(subjectDid)) + .mapError(ue => + serverError(Some(s"Unexpected error while updating issuance session with subject DID: ${ue.message}")) + ) + credentialDefinition <- ZIO + .fromOption(maybeCredentialDefinition) + .mapError(_ => badRequestUnsupportedCredentialType("No credential definition provided")) + validatedCredentialDefinition <- credentialIssuerService + .validateCredentialDefinition(credentialDefinition) + .mapError(ue => + serverError(Some(s"Unexpected error while validating credential definition: ${ue.message}")) + ) + credential <- credentialIssuerService + .issueJwtCredential( + session.issuingDid, + session.subjectDid, + session.claims, + maybeCredentialIdentifier, + validatedCredentialDefinition + ) + .mapError(ue => serverError(Some(s"Unexpected error while issuing credential: ${ue.message}"))) + } yield ImmediateCredentialResponse(credential.value) + case None => ZIO.fail(badRequestInvalidProof(jwt = "empty", details = "No proof provided")) + } + } + + override def createCredentialOffer( + ctx: RequestContext, + issuerId: UUID, + credentialOfferRequest: CredentialOfferRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialOfferResponse] = { + for { + issuingDid <- ZIO + .fromEither(PrismDID.fromString(credentialOfferRequest.issuingDID)) + .mapError(e => badRequest(detail = Some(s"Invalid DID: $e"))) + resp <- credentialIssuerService + .createCredentialOffer( + baseCredentialIssuerUrl(issuerId), + issuerId, + credentialOfferRequest.credentialConfigurationId, + issuingDid, + credentialOfferRequest.claims + ) + .map(offer => CredentialOfferResponse(offer.offerUri)) + .mapError(ue => + internalServerError(detail = Some(s"Unexpected error while creating credential offer: ${ue.message}")) + ) + } yield resp + } + + override def getNonce( + ctx: RequestContext, + request: NonceRequest + ): IO[ErrorResponse, NonceResponse] = { + credentialIssuerService + .getIssuanceSessionByIssuerState(request.issuerState) + .map(session => NonceResponse(session.nonce)) + .mapError(ue => + internalServerError(detail = Some(s"Unexpected error while creating credential offer: ${ue.message}")) + ) + } + + override def createCredentialIssuer( + ctx: RequestContext, + request: CreateCredentialIssuerRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuer] = + for { + authServerUrl <- parseURL(request.authorizationServer.url) + id = request.id.getOrElse(UUID.randomUUID()) + issuerToCreate = PolluxCredentialIssuer( + id, + authServerUrl, + request.authorizationServer.clientId, + request.authorizationServer.clientSecret + ) + issuer <- issuerMetadataService.createCredentialIssuer(issuerToCreate) + } yield issuer + + override def getCredentialIssuers( + ctx: RequestContext + ): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuerPage] = + val uri = ctx.request.uri + for { + issuers <- issuerMetadataService.getCredentialIssuers + } yield CredentialIssuerPage( + self = uri.toString(), + pageOf = PaginationUtils.composePageOfUri(uri).toString, + contents = issuers.map(i => i) + ) + + override def updateCredentialIssuer( + ctx: RequestContext, + issuerId: UUID, + request: PatchCredentialIssuerRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialIssuer] = + for { + maybeAuthServerUrl <- ZIO + .succeed(request.authorizationServer.flatMap(_.url)) + .flatMap { + case Some(url) => parseURL(url).asSome + case None => ZIO.none + } + issuer <- issuerMetadataService.updateCredentialIssuer( + issuerId, + maybeAuthServerUrl, + request.authorizationServer.flatMap(_.clientId), + request.authorizationServer.flatMap(_.clientSecret) + ) + } yield issuer: CredentialIssuer + + override def deleteCredentialIssuer( + ctx: RequestContext, + issuerId: UUID + ): ZIO[WalletAccessContext, ErrorResponse, Unit] = + for _ <- issuerMetadataService.deleteCredentialIssuer(issuerId) + yield () + + override def createCredentialConfiguration( + ctx: RequestContext, + issuerId: UUID, + request: CreateCredentialConfigurationRequest + ): ZIO[WalletAccessContext, ErrorResponse, CredentialConfiguration] = { + for { + credentialConfiguration <- issuerMetadataService.createCredentialConfiguration( + issuerId, + request.format, + request.configurationId, + request.schemaId + ) + } yield credentialConfiguration: CredentialConfiguration + } + + override def getCredentialConfiguration( + ctx: RequestContext, + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, ErrorResponse, CredentialConfiguration] = + for credentialConfiguration <- issuerMetadataService.getCredentialConfigurationById(issuerId, configurationId) + yield credentialConfiguration: CredentialConfiguration + + override def deleteCredentialConfiguration( + ctx: RequestContext, + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, ErrorResponse, Unit] = + issuerMetadataService.deleteCredentialConfiguration(issuerId, configurationId) + + override def getIssuerMetadata(ctx: RequestContext, issuerId: UUID): IO[ErrorResponse, IssuerMetadata] = { + for { + credentialIssuer <- issuerMetadataService.getCredentialIssuer(issuerId) + credentialConfigurations <- issuerMetadataService.getCredentialConfigurations(issuerId) + } yield IssuerMetadata.fromIssuer( + baseCredentialIssuerUrl(issuerId), + credentialIssuer, + credentialConfigurations + ) + } +} + +object CredentialIssuerControllerImpl { + val layer + : URLayer[AppConfig & OIDCCredentialIssuerService & OID4VCIIssuerMetadataService, CredentialIssuerController] = + ZLayer.fromZIO( + for { + agentBaseUrl <- ZIO.serviceWith[AppConfig](_.agent.httpEndpoint.publicEndpointUrl) + oidcIssuerService <- ZIO.service[OIDCCredentialIssuerService] + oidcIssuerMetadataService <- ZIO.service[OID4VCIIssuerMetadataService] + } yield CredentialIssuerControllerImpl(oidcIssuerService, oidcIssuerMetadataService, agentBaseUrl) + ) +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/IssuanceSession.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/IssuanceSession.scala new file mode 100644 index 0000000000..3769b44137 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/IssuanceSession.scala @@ -0,0 +1,19 @@ +package org.hyperledger.identus.oid4vci.domain + +import org.hyperledger.identus.castor.core.model.did.{DID, PrismDID} + +import java.util.UUID + +case class IssuanceSession( + id: UUID, + issuerId: UUID, + nonce: String, + issuerState: String, + schemaId: Option[String], + claims: zio.json.ast.Json, + subjectDid: Option[DID], + issuingDid: PrismDID, +) { + def withSchemaId(schemaId: String): IssuanceSession = copy(schemaId = Some(schemaId)) + def withSubjectDid(subjectDid: DID): IssuanceSession = copy(subjectDid = Some(subjectDid)) +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala new file mode 100644 index 0000000000..c26698506e --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/domain/Openid4VCIProofJwtOps.scala @@ -0,0 +1,72 @@ +package org.hyperledger.identus.oid4vci.domain + +import com.nimbusds.jose.{JOSEObjectType, JWSAlgorithm, JWSHeader, JWSObject, JWSSigner, Payload} +import org.hyperledger.identus.castor.core.model.did.{DID, LongFormPrismDID} +import org.hyperledger.identus.pollux.vc.jwt.JWT +import org.hyperledger.identus.pollux.vc.jwt.JwtSignerImplicits.* +import org.hyperledger.identus.shared.crypto.Secp256k1PrivateKey +import zio.{Task, ZIO} + +import java.util.UUID +import scala.jdk.CollectionConverters.* +import scala.util.Try + +trait Openid4VCIProofJwtOps { + + val jwtTypeName = "openid4vci-proof+jwt" + + val supportedAlgorithms: Set[String] = Set("ES256K") + + private def buildHeaderFromLongFormDid(longFormDID: LongFormPrismDID) = { + new JWSHeader.Builder(JWSAlgorithm.ES256K) + .keyID(longFormDID.toString) + .`type`(new JOSEObjectType(jwtTypeName)) + .build() + } + + private def buildPayload(nonce: String, aud: UUID, iat: Int) = { + new Payload(Map("nonce" -> nonce, "aud" -> aud.toString, "iat" -> iat).asJava) + } + + private def makeJwtProof(header: JWSHeader, payload: Payload, signer: JWSSigner): String = { + val jwt = new JWSObject(header, payload) + jwt.sign(signer) + jwt.serialize() + } + + def makeJwtProof( + longFormPrismDID: LongFormPrismDID, + nonce: String, + aud: UUID, + iat: Int, + privateKey: Secp256k1PrivateKey + ): JWT = { + val header = buildHeaderFromLongFormDid(longFormPrismDID) + val payload = buildPayload(nonce, aud, iat) + JWT(makeJwtProof(header, payload, privateKey.asJwtSigner)) + } + + def getKeyIdFromJwt(jwt: JWT): Task[String] = { + for { + jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))) + keyID = jwsObject.getHeader.getKeyID + _ <- ZIO.fail(new Exception("Key ID not found in JWT header")) when (keyID == null || keyID.isEmpty) + } yield keyID + } + + def getNonceFromJwt(jwt: JWT): Task[String] = { + for { + jwsObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))) + payload = jwsObject.getPayload.toJSONObject + nonce = payload.get("nonce").asInstanceOf[String] + _ <- ZIO.fail(new Exception("Nonce not found in JWT payload")) when (nonce == null || nonce.isEmpty) + } yield nonce + } + + def getSubjectDIDFromJwt(jwt: JWT): Task[DID] = { + for { + keyId <- getKeyIdFromJwt(jwt) + did <- ZIO.fromEither(DID.fromString(keyId)).mapError(e => new Exception(e)) + } yield did + } +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialConfiguration.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialConfiguration.scala new file mode 100644 index 0000000000..bcd83bcdd9 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialConfiguration.scala @@ -0,0 +1,43 @@ +package org.hyperledger.identus.oid4vci.http + +import org.hyperledger.identus.pollux.core.model.oid4vci.CredentialConfiguration as PolluxCredentialConfiguration +import sttp.tapir.Schema +import zio.json.* + +import java.time.{OffsetDateTime, ZoneOffset} +import scala.language.implicitConversions + +final case class CreateCredentialConfigurationRequest( + configurationId: String, + format: CredentialFormat, + schemaId: String, +) + +object CreateCredentialConfigurationRequest { + given schema: Schema[CreateCredentialConfigurationRequest] = Schema.derived + given encoder: JsonEncoder[CreateCredentialConfigurationRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CreateCredentialConfigurationRequest] = DeriveJsonDecoder.gen +} + +final case class CredentialConfiguration( + configurationId: String, + format: CredentialFormat, + scope: String, + schemaId: String, + createdAt: OffsetDateTime +) + +object CredentialConfiguration { + given schema: Schema[CredentialConfiguration] = Schema.derived + given encoder: JsonEncoder[CredentialConfiguration] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialConfiguration] = DeriveJsonDecoder.gen + + given Conversion[PolluxCredentialConfiguration, CredentialConfiguration] = cc => + CredentialConfiguration( + configurationId = cc.configurationId, + format = cc.format, + scope = cc.scope, + schemaId = cc.schemaId.toString(), + createdAt = cc.createdAt.atOffset(ZoneOffset.UTC), + ) +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialErrorResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialErrorResponse.scala new file mode 100644 index 0000000000..972cc7a0ea --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialErrorResponse.scala @@ -0,0 +1,109 @@ +package org.hyperledger.identus.oid4vci.http + +import org.hyperledger.identus.api.http.EndpointOutputs.statusCodeMatcher +import org.hyperledger.identus.api.http.ErrorResponse +import org.hyperledger.identus.iam.authentication.AuthenticationError +import sttp.model.StatusCode +import sttp.tapir.{oneOfVariantValueMatcher, Schema} +import sttp.tapir.json.zio.jsonBody +import sttp.tapir.Schema.annotations.encodedName +import zio.json.{jsonField, DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +import scala.util.Try + +type ExtendedErrorResponse = ErrorResponse | CredentialErrorResponse + +object ExtendedErrorResponse { + given schema: Schema[ExtendedErrorResponse] = Schema.schemaForEither[ErrorResponse, CredentialErrorResponse].as + given encoder: JsonEncoder[ExtendedErrorResponse] = + ErrorResponse.encoder + .orElseEither(CredentialErrorResponse.encoder) + .contramap { + case response: ErrorResponse => Left(response) + case response: CredentialErrorResponse => Right(response) + } + given decoder: JsonDecoder[ExtendedErrorResponse] = + ErrorResponse.decoder + .orElseEither(CredentialErrorResponse.decoder) + .map { + case Left(response) => response + case Right(response) => response + } +} + +case class CredentialErrorResponse( + error: CredentialErrorCode, + @jsonField("error_description") + @encodedName("error_description") + errorDescription: Option[String] = None, + @jsonField("c_nonce") + @encodedName("c_nonce") + nonce: Option[String] = None, + @jsonField("c_nonce_expires_in") + @encodedName("c_nonce_expires_in") + nonceExpiresIn: Option[Long] = None +) + +object CredentialErrorResponse { + given schema: Schema[CredentialErrorResponse] = Schema.derived + given encoder: JsonEncoder[CredentialErrorResponse] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialErrorResponse] = DeriveJsonDecoder.gen + + given Conversion[AuthenticationError, CredentialErrorResponse] = ae => { + import AuthenticationError.* + val error = ae match { + case _: InvalidCredentials => CredentialErrorCode.invalid_token + case _ => CredentialErrorCode.invalid_request + } + CredentialErrorResponse(error, Some(ae.message)) + } +} + +enum CredentialErrorCode { + case invalid_request + case invalid_token + case insufficient_scope + case invalid_credential_request + case unsupported_credential_type + case unsupported_credential_format + case invalid_proof + case invalid_encryption_parameters +} + +object CredentialErrorCode { + given schema: Schema[CredentialErrorCode] = Schema.derivedEnumeration.defaultStringBased + given encoder: JsonEncoder[CredentialErrorCode] = JsonEncoder.string.contramap(_.toString()) + given decoder: JsonDecoder[CredentialErrorCode] = + JsonDecoder.string.mapOrFail(s => + Try(CredentialErrorCode.valueOf(s)).toOption.toRight(s"Unknown CredentialErrorCode: $s") + ) + + implicit class CredentialErrorCodeOps(val credentialErrorCode: CredentialErrorCode) extends AnyVal { + def toHttpStatusCode: StatusCode = CredentialErrorCode.toHttpStatusCode(credentialErrorCode) + } + val toHttpStatusCode: PartialFunction[CredentialErrorCode, StatusCode] = { + case CredentialErrorCode.invalid_request => StatusCode.BadRequest + case CredentialErrorCode.invalid_token => StatusCode.Unauthorized + case CredentialErrorCode.insufficient_scope => StatusCode.Forbidden + case CredentialErrorCode.invalid_credential_request => StatusCode.BadRequest + case CredentialErrorCode.unsupported_credential_type => StatusCode.BadRequest + case CredentialErrorCode.invalid_proof => StatusCode.BadRequest + case CredentialErrorCode.invalid_encryption_parameters => StatusCode.BadRequest + } +} + +object CredentialRequestErrors { + def errorCodeMatcher( + credentialErrorCode: CredentialErrorCode + ): PartialFunction[Any, Boolean] = { + case CredentialErrorResponse(code, _, _, _) if code == credentialErrorCode => true + } + + val badRequest = oneOfVariantValueMatcher( + StatusCode.BadRequest, + jsonBody[ErrorResponse].description( + "The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, or is otherwise malformed" + ) + )(statusCodeMatcher(StatusCode.BadRequest)) + +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialIssuer.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialIssuer.scala new file mode 100644 index 0000000000..f5cdf6f3e5 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialIssuer.scala @@ -0,0 +1,68 @@ +package org.hyperledger.identus.oid4vci.http + +import org.hyperledger.identus.pollux.core.model.oid4vci.CredentialIssuer as PolluxCredentialIssuer +import sttp.tapir.Schema +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +import java.util.UUID + +case class CreateCredentialIssuerRequest( + id: Option[UUID], + authorizationServer: AuthorizationServer, +) + +object CreateCredentialIssuerRequest { + given schema: Schema[CreateCredentialIssuerRequest] = Schema.derived + given encoder: JsonEncoder[CreateCredentialIssuerRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CreateCredentialIssuerRequest] = DeriveJsonDecoder.gen +} + +case class AuthorizationServer(url: String, clientId: String, clientSecret: String) + +object AuthorizationServer { + given schema: Schema[AuthorizationServer] = Schema.derived + given encoder: JsonEncoder[AuthorizationServer] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[AuthorizationServer] = DeriveJsonDecoder.gen +} + +case class CredentialIssuer(id: UUID, authorizationServerUrl: String) + +case class PatchAuthorizationServer(url: Option[String], clientId: Option[String], clientSecret: Option[String]) + +object PatchAuthorizationServer { + given schema: Schema[PatchAuthorizationServer] = Schema.derived + given encoder: JsonEncoder[PatchAuthorizationServer] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[PatchAuthorizationServer] = DeriveJsonDecoder.gen +} + +case class PatchCredentialIssuerRequest(authorizationServer: Option[PatchAuthorizationServer] = None) + +object PatchCredentialIssuerRequest { + given schema: Schema[PatchCredentialIssuerRequest] = Schema.derived + given encoder: JsonEncoder[PatchCredentialIssuerRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[PatchCredentialIssuerRequest] = DeriveJsonDecoder.gen +} + +object CredentialIssuer { + given schema: Schema[CredentialIssuer] = Schema.derived + given encoder: JsonEncoder[CredentialIssuer] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialIssuer] = DeriveJsonDecoder.gen + + given Conversion[PolluxCredentialIssuer, CredentialIssuer] = domain => + CredentialIssuer(domain.id, domain.authorizationServer.toString) +} + +case class CredentialIssuerPage( + self: String, + kind: String = "CredentialIssuerPage", + pageOf: String, + next: Option[String] = None, + previous: Option[String] = None, + contents: Seq[CredentialIssuer] +) + +object CredentialIssuerPage { + given schema: Schema[CredentialIssuerPage] = Schema.derived + given encoder: JsonEncoder[CredentialIssuerPage] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialIssuerPage] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialOffer.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialOffer.scala new file mode 100644 index 0000000000..73d0c3a371 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialOffer.scala @@ -0,0 +1,43 @@ +package org.hyperledger.identus.oid4vci.http + +import sttp.tapir.Schema +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +case class CredentialOffer( + credential_issuer: String, + credential_configuration_ids: Seq[String], + grants: Option[CredentialOfferGrant], +) { + def offerUri: String = { + val offerJson = CredentialOffer.encoder.encodeJson(this).toString() + val encodedOffer = URLEncoder.encode(offerJson, StandardCharsets.UTF_8) + s"openid-credential-offer://?credential_offer=$encodedOffer" + } +} + +object CredentialOffer { + given schema: Schema[CredentialOffer] = Schema.derived + given encoder: JsonEncoder[CredentialOffer] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialOffer] = DeriveJsonDecoder.gen +} + +case class CredentialOfferGrant( + authorization_code: CredentialOfferAuthorizationGrant +) + +object CredentialOfferGrant { + given schema: Schema[CredentialOfferGrant] = Schema.derived + given encoder: JsonEncoder[CredentialOfferGrant] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialOfferGrant] = DeriveJsonDecoder.gen +} + +case class CredentialOfferAuthorizationGrant(issuer_state: Option[String]) + +object CredentialOfferAuthorizationGrant { + given schema: Schema[CredentialOfferAuthorizationGrant] = Schema.derived + given encoder: JsonEncoder[CredentialOfferAuthorizationGrant] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialOfferAuthorizationGrant] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialOfferRequest.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialOfferRequest.scala new file mode 100644 index 0000000000..eabac5d60d --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialOfferRequest.scala @@ -0,0 +1,25 @@ +package org.hyperledger.identus.oid4vci.http + +import sttp.tapir.json.zio.schemaForZioJsonValue +import sttp.tapir.Schema +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +case class CredentialOfferRequest( + credentialConfigurationId: String, + issuingDID: String, + claims: zio.json.ast.Json, +) + +object CredentialOfferRequest { + given schema: Schema[CredentialOfferRequest] = Schema.derived + given encoder: JsonEncoder[CredentialOfferRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialOfferRequest] = DeriveJsonDecoder.gen +} + +case class CredentialOfferResponse(credentialOffer: String) + +object CredentialOfferResponse { + given schema: Schema[CredentialOfferResponse] = Schema.derived + given encoder: JsonEncoder[CredentialOfferResponse] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialOfferResponse] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialRequest.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialRequest.scala new file mode 100644 index 0000000000..aa416a53e2 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialRequest.scala @@ -0,0 +1,219 @@ +package org.hyperledger.identus.oid4vci.http + +import org.hyperledger.identus.pollux.core.model.CredentialFormat as PolluxCredentialFormat +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.encodedName +import zio.json.* + +// The idea to have a hierarchy of requests is to be able to use the same endpoint for different types of requests +// This trait contains the common fields for all the requests +@jsonDiscriminator("format") +sealed trait CredentialRequest { + // REQUIRED when the credential_identifiers parameter was not returned from the Token Response. + // It MUST NOT be used otherwise + // It not an optional in this implementation to help to define the serializer and schema + val format: CredentialFormat + // Object containing the proof of possession of the cryptographic key material the issued Credential would be bound to. + // The proof object is REQUIRED if the proof_types_supported parameter is non-empty and present in the credential_configurations_supported parameter of the Issuer metadata for the requested Credential. + val proof: Option[Proof] + // REQUIRED when credential_identifiers parameter was returned from the Token Response. It MUST NOT be used otherwise. + // It is a String that identifies a Credential that is being requested to be issued. + val credentialIdentifier: Option[String] + // OPTIONAL. Object containing information for encrypting the Credential Response. + // If this request element is not present, the corresponding credential response returned is not encrypted + val credentialResponseEncryption: Option[CredentialResponseEncryption] +} + +object CredentialRequest { + given schema: Schema[CredentialRequest] = Schema + .oneOfUsingField[CredentialRequest, CredentialFormat](_.format, _.toString)( + CredentialFormat.jwt_vc_json -> JwtCredentialRequest.schema, + CredentialFormat.anoncreds -> AnoncredsCredentialRequest.schema + ) + given encoder: JsonEncoder[CredentialRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialRequest] = DeriveJsonDecoder.gen +} + +@jsonHint(CredentialFormat.jwt_vc_json.toString) +case class JwtCredentialRequest( + format: CredentialFormat, + proof: Option[Proof], + @jsonField("credential_identifier") + @encodedName("credential_identifier") + credentialIdentifier: Option[String], + @jsonField("credential_response_encryption") + @encodedName("credential_response_encryption") + credentialResponseEncryption: Option[CredentialResponseEncryption], + // REQUIRED when the format parameter is present in the Credential Request. + // It MUST NOT be used otherwise. It is an object containing the detailed description of the Credential type. + @jsonField("credential_definition") + @encodedName("credential_definition") + credentialDefinition: Option[CredentialDefinition] +) extends CredentialRequest + +object JwtCredentialRequest { + given schema: Schema[JwtCredentialRequest] = Schema.derived + given encoder: JsonEncoder[JwtCredentialRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[JwtCredentialRequest] = DeriveJsonDecoder.gen +} + +@jsonHint(CredentialFormat.anoncreds.toString) +case class AnoncredsCredentialRequest( + format: CredentialFormat, + proof: Option[Proof], + @jsonField("credential_identifier") + @encodedName("credential_identifier") + credentialIdentifier: Option[String], + @jsonField("credential_response_encryption") + @encodedName("credential_response_encryption") + credentialResponseEncryption: Option[CredentialResponseEncryption], + // REQUIRED when the format parameter is present in the Credential Request. + // It MUST NOT be used otherwise. It is an object containing the detailed description of the Credential type. + @jsonField("credential_definition") + @encodedName("credential_definition") + credentialDefinition: Option[CredentialDefinition], + anoncreds: String // TODO: it's a fake field for now +) extends CredentialRequest + +object AnoncredsCredentialRequest { + given schema: Schema[AnoncredsCredentialRequest] = Schema.derived + + given encoder: JsonEncoder[AnoncredsCredentialRequest] = DeriveJsonEncoder.gen + + given decoder: JsonDecoder[AnoncredsCredentialRequest] = DeriveJsonDecoder.gen +} + +case class CredentialResponseEncryption(jwk: String, alg: String, enc: String) +object CredentialResponseEncryption { + given schema: Schema[CredentialResponseEncryption] = Schema.derived + given encoder: JsonEncoder[CredentialResponseEncryption] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialResponseEncryption] = DeriveJsonDecoder.gen +} + +type CredentialSubject = Map[String, ClaimDescriptor] + +case class CredentialDefinition( + `@context`: Option[Seq[String]], + `type`: Seq[String], + credentialSubject: Option[CredentialSubject] +) +object CredentialDefinition { + given schema: Schema[CredentialDefinition] = Schema.derived + given encoder: JsonEncoder[CredentialDefinition] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CredentialDefinition] = DeriveJsonDecoder.gen +} +case class ClaimDescriptor( + mandatory: Option[Boolean], + @jsonField("value_type") + @encodedName("value_type") + valueType: Option[String], + display: Seq[Localization] +) + +object ClaimDescriptor { + given schema: Schema[ClaimDescriptor] = Schema.derived + given encoder: JsonEncoder[ClaimDescriptor] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[ClaimDescriptor] = DeriveJsonDecoder.gen +} + +case class Localization(name: String, locale: String) + +object Localization { + given schema: Schema[Localization] = Schema.derived + given encoder: JsonEncoder[Localization] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[Localization] = DeriveJsonDecoder.gen +} + +@jsonDiscriminator("proof_type") +sealed trait Proof { + val proof_type: ProofType +} + +object Proof { + given schema: Schema[Proof] = Schema + .oneOfUsingField[Proof, ProofType](_.proof_type, _.toString)( + ProofType.jwt -> JwtProof.schema, + ProofType.cwt -> CwtProof.schema, + ProofType.ldp_vp -> LdpProof.schema + ) + given encoder: JsonEncoder[Proof] = DeriveJsonEncoder.gen[Proof] + given decoder: JsonDecoder[Proof] = DeriveJsonDecoder.gen[Proof] +} + +@jsonHint(ProofType.jwt.toString) +case class JwtProof( + proof_type: ProofType, + jwt: String +) extends Proof { + def this(jwt: String) = this(proof_type = ProofType.jwt, jwt) +} + +object JwtProof { + given schema: Schema[JwtProof] = Schema.derived + given encoder: JsonEncoder[JwtProof] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[JwtProof] = DeriveJsonDecoder.gen +} + +@jsonHint(ProofType.cwt.toString) +case class CwtProof( + proof_type: ProofType, + cwt: String +) extends Proof + +object CwtProof { + given schema: Schema[CwtProof] = Schema.derived + given encoder: JsonEncoder[CwtProof] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[CwtProof] = DeriveJsonDecoder.gen +} + +@jsonHint(ProofType.ldp_vp.toString) +case class LdpProof( + proof_type: ProofType, + vp: String +) extends Proof + +object LdpProof { + given schema: Schema[LdpProof] = Schema.derived + given encoder: JsonEncoder[LdpProof] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[LdpProof] = DeriveJsonDecoder.gen +} + +enum CredentialFormat { + case jwt_vc_json + case anoncreds + case `vc+sd-jwt` +} + +object CredentialFormat { + given schema: Schema[CredentialFormat] = Schema.derivedEnumeration.defaultStringBased + given encoder: JsonEncoder[CredentialFormat] = JsonEncoder[String].contramap(_.toString) + given decoder: JsonDecoder[CredentialFormat] = JsonDecoder[String].mapOrFail { s => + CredentialFormat.values.find(_.toString == s).toRight(s"Unknown CredentialFormat: $s") + } + + given Conversion[PolluxCredentialFormat, CredentialFormat] = { + case PolluxCredentialFormat.JWT => CredentialFormat.jwt_vc_json + case PolluxCredentialFormat.AnonCreds => CredentialFormat.anoncreds + case PolluxCredentialFormat.SDJWT => CredentialFormat.`vc+sd-jwt` + } + + given Conversion[CredentialFormat, PolluxCredentialFormat] = { + case CredentialFormat.jwt_vc_json => PolluxCredentialFormat.JWT + case CredentialFormat.anoncreds => PolluxCredentialFormat.AnonCreds + case CredentialFormat.`vc+sd-jwt` => PolluxCredentialFormat.SDJWT + } +} + +enum ProofType { + case jwt + case cwt + case ldp_vp +} + +object ProofType { + given schema: Schema[ProofType] = Schema.derivedEnumeration.defaultStringBased + given encoder: JsonEncoder[ProofType] = JsonEncoder[String].contramap(_.toString) + given decoder: JsonDecoder[ProofType] = JsonDecoder[String].mapOrFail { s => + ProofType.values.find(_.toString == s).toRight(s"Unknown ProofType: $s") + } +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialResponse.scala new file mode 100644 index 0000000000..715fff5092 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/CredentialResponse.scala @@ -0,0 +1,53 @@ +package org.hyperledger.identus.oid4vci.http + +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.encodedName +import zio.json.* +import zio.json.ast.Json + +sealed trait CredentialResponse { + // OPTIONAL. String containing a nonce to be used to create a proof of possession of key material when requesting a Credential (see Section 7.2). + // When received, the Wallet MUST use this nonce value for its subsequent Credential Requests until the Credential Issuer provides a fresh nonce. + val nonce: Option[String] + // OPTIONAL. String identifying an issued Credential that the Wallet includes in the Notification Request as defined in Section 10.1. + // This parameter MUST NOT be present if credential parameter is not present + val nonceExpiresIn: Option[Int] +} + +object CredentialResponse { + given schema: Schema[CredentialResponse] = Schema.derived + given decoder: JsonDecoder[CredentialResponse] = + ImmediateCredentialResponse.decoder + .orElse(DeferredCredentialResponse.decoder.widen) + given encoder: JsonEncoder[CredentialResponse] = JsonEncoder[Json].contramap { resp => + val jsonAst = resp match { + case i: ImmediateCredentialResponse => i.toJsonAST + case i: DeferredCredentialResponse => i.toJsonAST + } + jsonAst.getOrElse(throw new RuntimeException(s"Failed to encode CredentialResponse to JSON: ${resp}")) + } +} + +case class ImmediateCredentialResponse( + credential: String, + @jsonField("c_nonce") @encodedName("c_nonce") nonce: Option[String] = None, + @jsonField("c_nonce_expires_in") @encodedName("c_nonce_expires_in") nonceExpiresIn: Option[Int] = None +) extends CredentialResponse + +object ImmediateCredentialResponse { + given schema: Schema[ImmediateCredentialResponse] = Schema.derived + given encoder: JsonEncoder[ImmediateCredentialResponse] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[ImmediateCredentialResponse] = DeriveJsonDecoder.gen +} + +case class DeferredCredentialResponse( + @jsonField("transaction_id") @encodedName("transaction_id") transactionId: String, + @jsonField("c_nonce") @encodedName("c_nonce") nonce: Option[String], + @jsonField("c_nonce_expires_in") @encodedName("c_nonce_expires_in") nonceExpiresIn: Option[Int] +) extends CredentialResponse + +object DeferredCredentialResponse { + given schema: Schema[DeferredCredentialResponse] = Schema.derived + given encoder: JsonEncoder[DeferredCredentialResponse] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[DeferredCredentialResponse] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/IssuerMetadata.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/IssuerMetadata.scala new file mode 100644 index 0000000000..1b28b6abaa --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/IssuerMetadata.scala @@ -0,0 +1,79 @@ +package org.hyperledger.identus.oid4vci.http + +import org.hyperledger.identus.pollux.core.model.oid4vci as pollux +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.encodedName +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +import java.net.URL +import scala.language.implicitConversions + +case class IssuerMetadata( + credential_issuer: String, + authorization_servers: Option[Seq[String]], + credential_endpoint: String, + credential_configurations_supported: Map[String, SupportedCredentialConfiguration] +) + +object IssuerMetadata { + given schema: Schema[IssuerMetadata] = Schema.derived + given encoder: JsonEncoder[IssuerMetadata] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[IssuerMetadata] = DeriveJsonDecoder.gen + + def fromIssuer( + credentialIssuerBaseUrl: URL, + issuer: pollux.CredentialIssuer, + credentialConfigurations: Seq[pollux.CredentialConfiguration] + ): IssuerMetadata = { + IssuerMetadata( + credential_issuer = credentialIssuerBaseUrl.toString(), + authorization_servers = Some(Seq(issuer.authorizationServer.toString())), + credential_endpoint = s"$credentialIssuerBaseUrl/credentials", + credential_configurations_supported = + credentialConfigurations.map(cc => (cc.configurationId, cc: SupportedCredentialConfiguration)).toMap + ) + } +} + +final case class SupportedCredentialConfiguration( + format: CredentialFormat, + scope: String, + credential_definition: CredentialDefinition, + cryptographic_binding_methods_supported: Seq[String] = Seq("did:prism"), + credential_signing_alg_values_supported: Seq[String] = Seq("ES256K"), + proof_types_supported: SupportProofType = + SupportProofType(jwt = ProofTypeConfiguration(proof_signing_alg_values_supported = Seq("ES256K"))) +) + +object SupportedCredentialConfiguration { + given schema: Schema[SupportedCredentialConfiguration] = Schema.derived + given encoder: JsonEncoder[SupportedCredentialConfiguration] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[SupportedCredentialConfiguration] = DeriveJsonDecoder.gen + + given Conversion[pollux.CredentialConfiguration, SupportedCredentialConfiguration] = cc => + SupportedCredentialConfiguration( + format = cc.format, + scope = cc.scope, + credential_definition = CredentialDefinition( + `@context` = Some(Seq("https://www.w3.org/2018/credentials/v1")), + `type` = Seq("VerifiableCredential"), + credentialSubject = None + ) + ) +} + +final case class SupportProofType(jwt: ProofTypeConfiguration) + +object SupportProofType { + given schema: Schema[SupportProofType] = Schema.derived + given encoder: JsonEncoder[SupportProofType] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[SupportProofType] = DeriveJsonDecoder.gen +} + +final case class ProofTypeConfiguration(proof_signing_alg_values_supported: Seq[String]) + +object ProofTypeConfiguration { + given schema: Schema[ProofTypeConfiguration] = Schema.derived + given encoder: JsonEncoder[ProofTypeConfiguration] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[ProofTypeConfiguration] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/NonceRequest.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/NonceRequest.scala new file mode 100644 index 0000000000..ef9ddc9468 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/NonceRequest.scala @@ -0,0 +1,12 @@ +package org.hyperledger.identus.oid4vci.http + +import sttp.tapir.Schema +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +case class NonceRequest(issuerState: String) + +object NonceRequest { + given schema: Schema[NonceRequest] = Schema.derived + given encoder: JsonEncoder[NonceRequest] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[NonceRequest] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/NonceResponse.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/NonceResponse.scala new file mode 100644 index 0000000000..33239d8089 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/http/NonceResponse.scala @@ -0,0 +1,19 @@ +package org.hyperledger.identus.oid4vci.http + +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.encodedName +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder} + +case class NonceResponse( + nonce: String, + nonceExpiresIn: Long = 86400 +) { + require(nonce.nonEmpty, "nonce must not be empty") + require(nonceExpiresIn > 0, "nonceExpiresIn must be greater than 0") +} + +object NonceResponse { + given schema: Schema[NonceResponse] = Schema.derived + given encoder: JsonEncoder[NonceResponse] = DeriveJsonEncoder.gen + given decoder: JsonDecoder[NonceResponse] = DeriveJsonDecoder.gen +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala new file mode 100644 index 0000000000..02b3c47983 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/service/OIDCCredentialIssuerService.scala @@ -0,0 +1,285 @@ +package org.hyperledger.identus.oid4vci.service + +import io.circe.parser.parse +import io.circe.Json +import org.hyperledger.identus.agent.walletapi.storage.DIDNonSecretStorage +import org.hyperledger.identus.castor.core.model.did.{DID, PrismDID, VerificationRelationship} +import org.hyperledger.identus.oid4vci.domain.IssuanceSession +import org.hyperledger.identus.oid4vci.http.* +import org.hyperledger.identus.oid4vci.storage.IssuanceSessionStorage +import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.core.service.{ + CredentialService, + OID4VCIIssuerMetadataService, + OID4VCIIssuerMetadataServiceError, + URIDereferencer +} +import org.hyperledger.identus.pollux.vc.jwt.{ + DID as PolluxDID, + DidResolver, + Issuer, + JWT, + JWTVerification, + JwtCredential, + W3cCredentialPayload +} +import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import zio.* + +import java.net.URL +import java.time.Instant +import java.util.UUID +import scala.util.Try + +// TODO: move to pollux +// OIDC prefix is added to the service name to avoid name conflicts with a similar service CredentialIssuerService +// It would be nice to refactor these services and merge them into one +trait OIDCCredentialIssuerService { + + import OIDCCredentialIssuerService.Error + import OIDCCredentialIssuerService.Errors.* + + def verifyJwtProof(jwt: JWT): IO[InvalidProof, Boolean] + + def validateCredentialDefinition( + credentialDefinition: CredentialDefinition + ): IO[UnexpectedError, CredentialDefinition] + + def issueJwtCredential( + issuingDID: PrismDID, + subjectDID: Option[DID], + claims: zio.json.ast.Json, + credentialIdentifier: Option[String], + credentialDefinition: CredentialDefinition, + ): IO[Error, JWT] + + def createCredentialOffer( + credentialIssuerBaseUrl: URL, + issuerId: UUID, + credentialConfigurationId: String, + issuingDID: PrismDID, + claims: zio.json.ast.Json, + ): ZIO[WalletAccessContext, Error, CredentialOffer] + + def getIssuanceSessionByIssuerState(issuerState: String): IO[Error, IssuanceSession] + + def getIssuanceSessionByNonce(nonce: String): IO[Error, IssuanceSession] + + def updateIssuanceSession(issuanceSession: IssuanceSession): IO[Error, IssuanceSession] +} + +object OIDCCredentialIssuerService { + trait Error { + def message: String + } + + // TODO: use shared Failure trait + object Errors { + case class InvalidProof(message: String) extends Error + + case class DIDResolutionError(message: String) extends Error + + case class CredentialConfigurationNotFound(issuerId: UUID, credentialConfigurationId: String) extends Error { + override def message: String = + s"Credential configuration with id $credentialConfigurationId not found for issuer $issuerId" + } + + case class CredentialSchemaError(cause: org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError) + extends Error { + override def message: String = cause.userFacingMessage + } + + case class ServiceError(message: String) extends Error + + case class UnexpectedError(cause: Throwable) extends Error { + override def message: String = cause.getMessage + } + } +} + +case class OIDCCredentialIssuerServiceImpl( + didNonSecretStorage: DIDNonSecretStorage, + credentialService: CredentialService, + issuerMetadataService: OID4VCIIssuerMetadataService, + issuanceSessionStorage: IssuanceSessionStorage, + didResolver: DidResolver, + uriDereferencer: URIDereferencer, +) extends OIDCCredentialIssuerService { + + import OIDCCredentialIssuerService.Error + import OIDCCredentialIssuerService.Errors.* + + override def verifyJwtProof(jwt: JWT): IO[InvalidProof, Boolean] = { + for { + verifiedJwtSignature <- JWTVerification + .validateEncodedJwtWithKeyId( + jwt, + proofPurpose = Some(VerificationRelationship.AssertionMethod), + didResolver = didResolver + ) + .mapError(InvalidProof.apply) + _ <- verifiedJwtSignature.toZIO.mapError(InvalidProof.apply) + } yield true + } + + override def validateCredentialDefinition( + credentialDefinition: CredentialDefinition + ): IO[UnexpectedError, CredentialDefinition] = { + ZIO.succeed(credentialDefinition) // TODO: implement + } + + override def issueJwtCredential( + issuingDID: PrismDID, + subjectDID: Option[DID], + claims: zio.json.ast.Json, + credentialIdentifier: Option[String], + credentialDefinition: CredentialDefinition + ): IO[Error, JWT] = { + for { + wac <- didNonSecretStorage + .getPrismDidWalletId(issuingDID) + .flatMap(ZIO.fromOption) + .mapError(_ => ServiceError(s"Failed to get wallet ID for DID: $issuingDID")) + .map(WalletAccessContext.apply) + + jwtIssuer <- credentialService + .getJwtIssuer(issuingDID, VerificationRelationship.AssertionMethod) + .provideSomeLayer(ZLayer.succeed(wac)) + + jwtVC <- buildJwtVerifiableCredential( + jwtIssuer.did, + subjectDID, + credentialIdentifier, + credentialDefinition, + claims + ) + jwt <- issueJwtVC(jwtIssuer, jwtVC) + } yield jwt + } + + def buildJwtVerifiableCredential( + issuerDid: PolluxDID, + subjectDid: Option[DID], + credentialIdentifier: Option[String], + credentialDefinition: CredentialDefinition, + claims: zio.json.ast.Json + ): IO[Error, W3cCredentialPayload] = { + val credential = W3cCredentialPayload( + `@context` = Set( + "https://www.w3.org/2018/credentials/v1" + // TODO: Add schemaID from schema registry + ) ++ credentialDefinition.`@context`.getOrElse(Nil), // TODO: Figure out how to validate the context ^^^ + maybeId = credentialIdentifier, + `type` = Set( + "VerifiableCredential" + ) ++ credentialDefinition.`type`, // TODO: This information should come from Schema registry by record.schemaId + issuer = issuerDid, + issuanceDate = Instant.now(), + maybeExpirationDate = None, // TODO: Add expiration date + maybeCredentialSchema = None, // TODO: Add schema from schema registry + credentialSubject = buildCredentialSubject(subjectDid, claims), + maybeCredentialStatus = None, // TODO: Add credential status + maybeRefreshService = None, // TODO: Add refresh service + maybeEvidence = None, // TODO: Add evidence + maybeTermsOfUse = None // TODO: Add terms of use + ) + + ZIO.succeed(credential) // TODO: there might be other calls to fill the VC claims from the session, etc + } + + private def simpleZioToCirce(json: zio.json.ast.Json): Json = + parse(json.toString).toOption.get + + private def buildCredentialSubject(subjectDid: Option[DID], claims: zio.json.ast.Json): Json = { + val subjectClaims = simpleZioToCirce(claims) + subjectDid.fold(subjectClaims)(did => Json.obj("id" -> Json.fromString(did.toString)) deepMerge subjectClaims) + } + + def issueJwtVC(issuer: Issuer, payload: W3cCredentialPayload): IO[Error, JWT] = { + ZIO + .fromTry(Try(JwtCredential.encodeJwt(payload.toJwtCredentialPayload, issuer))) + .mapError(e => ServiceError(s"Failed to issue JWT: ${e.getMessage}")) + } + + override def getIssuanceSessionByIssuerState( + issuerState: String + ): IO[Error, IssuanceSession] = + issuanceSessionStorage + .getByIssuerState(issuerState) + .mapError(e => ServiceError(s"Failed to get issuance session: ${e.message}")) + .someOrFail(ServiceError(s"The IssuanceSession with the issuerState $issuerState does not exist")) + + override def createCredentialOffer( + credentialIssuerBaseUrl: URL, + issuerId: UUID, + credentialConfigurationId: String, + issuingDID: PrismDID, + claims: zio.json.ast.Json + ): ZIO[WalletAccessContext, Error, CredentialOffer] = + for { + schemaId <- issuerMetadataService + .getCredentialConfigurationById(issuerId, credentialConfigurationId) + .mapError { case _: OID4VCIIssuerMetadataServiceError.CredentialConfigurationNotFound => + CredentialConfigurationNotFound(issuerId, credentialConfigurationId) + } + .map(_.schemaId) + _ <- CredentialSchema + .validateJWTCredentialSubject(schemaId.toString(), simpleZioToCirce(claims).noSpaces, uriDereferencer) + .mapError(e => CredentialSchemaError(e)) + session <- buildNewIssuanceSession(issuerId, issuingDID, claims) + _ <- issuanceSessionStorage + .start(session) + .mapError(e => ServiceError(s"Failed to start issuance session: ${e.message}")) + } yield CredentialOffer( + credential_issuer = credentialIssuerBaseUrl.toString(), + credential_configuration_ids = Seq(credentialConfigurationId), + grants = Some( + CredentialOfferGrant( + authorization_code = CredentialOfferAuthorizationGrant(issuer_state = Some(session.issuerState)) + ) + ) + ) + + def getIssuanceSessionByNonce(nonce: String): IO[Error, IssuanceSession] = { + issuanceSessionStorage + .getByNonce(nonce) + .mapError(e => ServiceError(s"Failed to get issuance session: ${e.message}")) + .someOrFail(ServiceError(s"The IssuanceSession with the nonce $nonce does not exist")) + } + + override def updateIssuanceSession(issuanceSession: IssuanceSession): IO[Error, IssuanceSession] = { + issuanceSessionStorage + .update(issuanceSession) + .mapError(e => ServiceError(s"Failed to update issuance session: ${e.message}")) + } + + private def buildNewIssuanceSession( + issuerId: UUID, + issuerDid: PrismDID, + claims: zio.json.ast.Json + ): UIO[IssuanceSession] = { + for { + id <- ZIO.random.flatMap(_.nextUUID) + nonce <- ZIO.random.flatMap(_.nextUUID) + issuerState <- ZIO.random.flatMap(_.nextUUID) + } yield IssuanceSession( + id = id, + issuerId = issuerId, + nonce = nonce.toString, + issuerState = issuerState.toString, + claims = claims, + schemaId = None, // FIXME: populate correct value + subjectDid = None, // FIXME: populate correct value + issuingDid = issuerDid, + ) + } +} + +object OIDCCredentialIssuerServiceImpl { + val layer: URLayer[ + DIDNonSecretStorage & CredentialService & IssuanceSessionStorage & DidResolver & URIDereferencer & + OID4VCIIssuerMetadataService, + OIDCCredentialIssuerService + ] = + ZLayer.fromFunction(OIDCCredentialIssuerServiceImpl(_, _, _, _, _, _)) +} diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/storage/IssuanceSessionStorage.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/storage/IssuanceSessionStorage.scala new file mode 100644 index 0000000000..92a6d97c80 --- /dev/null +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/oid4vci/storage/IssuanceSessionStorage.scala @@ -0,0 +1,71 @@ +package org.hyperledger.identus.oid4vci.storage + +import org.hyperledger.identus.oid4vci.domain.IssuanceSession +import zio.{IO, ULayer, ZIO, ZLayer} + +import scala.collection.concurrent.TrieMap + +trait IssuanceSessionStorage { + def start(issuanceSession: IssuanceSession): IO[IssuanceSessionStorage.Error, IssuanceSession] + def getByNonce(nonce: String): IO[IssuanceSessionStorage.Error, Option[IssuanceSession]] + def getByIssuerState(issuerState: String): IO[IssuanceSessionStorage.Error, Option[IssuanceSession]] + def update(issuanceSession: IssuanceSession): IO[IssuanceSessionStorage.Error, IssuanceSession] +} + +object IssuanceSessionStorage { + trait Error { + def message: String + } + + object Errors { + case class IssuanceSessionNotFound(nonce: String) extends Error { + override def message: String = s"Issuance session with nonce $nonce not found" + } + + case class IssuanceSessionAlreadyExists(nonce: String) extends Error { + override def message: String = s"Issuance session with nonce $nonce already exists" + } + + case class CouldNotStartIssuanceSession(issuanceSession: IssuanceSession, details: String) extends Error { + override def message: String = s"Could not start issuance session $issuanceSession. Details: $details" + } + } +} + +case class InMemoryIssuanceSessionService() extends IssuanceSessionStorage { + private val issuanceSessions: TrieMap[String, IssuanceSession] = TrieMap.empty + + override def start(issuanceSession: IssuanceSession): IO[IssuanceSessionStorage.Error, IssuanceSession] = { + issuanceSessions + .put(issuanceSession.nonce, issuanceSession) + .fold(ZIO.succeed(issuanceSession))(foundIssuanceSession => + ZIO.fail(IssuanceSessionStorage.Errors.IssuanceSessionAlreadyExists(issuanceSession.nonce)) + ) + } + + override def getByNonce(nonce: String): IO[IssuanceSessionStorage.Error, Option[IssuanceSession]] = { + issuanceSessions.get(nonce) match { + case Some(issuanceSession) => ZIO.succeed(Some(issuanceSession)) + case None => ZIO.succeed(None) + } + } + + override def getByIssuerState(issuerState: String): IO[IssuanceSessionStorage.Error, Option[IssuanceSession]] = { + issuanceSessions.values.find(_.issuerState == issuerState) match { + case Some(issuanceSession) => ZIO.succeed(Some(issuanceSession)) + case None => ZIO.succeed(None) + } + } + + override def update(issuanceSession: IssuanceSession): IO[IssuanceSessionStorage.Error, IssuanceSession] = { + issuanceSessions + .put(issuanceSession.nonce, issuanceSession) + .fold(ZIO.fail(IssuanceSessionStorage.Errors.IssuanceSessionNotFound(issuanceSession.nonce)))(_ => + ZIO.succeed(issuanceSession) + ) + } +} + +object InMemoryIssuanceSessionService { + val layer: ULayer[IssuanceSessionStorage] = ZLayer.succeed(InMemoryIssuanceSessionService()) +} diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala index 9122fe4cc1..42f55edbc5 100644 --- a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/api/util/Tapir2StaticOAS.scala @@ -6,10 +6,11 @@ import org.hyperledger.identus.castor.controller.{DIDController, DIDRegistrarCon import org.hyperledger.identus.connect.controller.ConnectionController import org.hyperledger.identus.credentialstatus.controller.CredentialStatusController import org.hyperledger.identus.event.controller.EventController -import org.hyperledger.identus.iam.authentication.DefaultAuthenticator +import org.hyperledger.identus.iam.authentication.{DefaultAuthenticator, Oid4vciAuthenticatorFactory} import org.hyperledger.identus.iam.entity.http.controller.EntityController import org.hyperledger.identus.iam.wallet.http.controller.WalletManagementController import org.hyperledger.identus.issue.controller.IssueController +import org.hyperledger.identus.oid4vci.controller.CredentialIssuerController import org.hyperledger.identus.pollux.credentialdefinition.controller.CredentialDefinitionController import org.hyperledger.identus.pollux.credentialschema.controller.{ CredentialSchemaController, @@ -55,7 +56,9 @@ object Tapir2StaticOAS extends ZIOAppDefault { ZLayer.succeed(mock[EntityController]) ++ ZLayer.succeed(mock[WalletManagementController]) ++ ZLayer.succeed(mock[DefaultAuthenticator]) ++ - ZLayer.succeed(mock[EventController]) + ZLayer.succeed(mock[EventController]) ++ + ZLayer.succeed(mock[CredentialIssuerController]) ++ + ZLayer.succeed(mock[Oid4vciAuthenticatorFactory]) ) } diff --git a/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/oid4vci/domain/OIDCCredentialIssuerServiceSpec.scala b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/oid4vci/domain/OIDCCredentialIssuerServiceSpec.scala new file mode 100644 index 0000000000..6f1f4aa90d --- /dev/null +++ b/cloud-agent/service/server/src/test/scala/org/hyperledger/identus/oid4vci/domain/OIDCCredentialIssuerServiceSpec.scala @@ -0,0 +1,214 @@ +package org.hyperledger.identus.oid4vci.domain + +import com.nimbusds.jose.* +import org.hyperledger.identus.agent.walletapi.memory.GenericSecretStorageInMemory +import org.hyperledger.identus.agent.walletapi.service.{ManagedDIDService, MockManagedDIDService} +import org.hyperledger.identus.agent.walletapi.storage.{DIDNonSecretStorage, MockDIDNonSecretStorage} +import org.hyperledger.identus.castor.core.model.did.{DID, PrismDID, VerificationRelationship} +import org.hyperledger.identus.castor.core.service.{DIDService, MockDIDService} +import org.hyperledger.identus.oid4vci.http.{ClaimDescriptor, CredentialDefinition, Localization} +import org.hyperledger.identus.oid4vci.service.{OIDCCredentialIssuerService, OIDCCredentialIssuerServiceImpl} +import org.hyperledger.identus.oid4vci.storage.InMemoryIssuanceSessionService +import org.hyperledger.identus.pollux.core.model.oid4vci.CredentialConfiguration +import org.hyperledger.identus.pollux.core.model.CredentialFormat +import org.hyperledger.identus.pollux.core.repository.{ + CredentialRepository, + CredentialRepositoryInMemory, + CredentialStatusListRepositoryInMemory +} +import org.hyperledger.identus.pollux.core.service.* +import org.hyperledger.identus.pollux.vc.jwt.PrismDidResolver +import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import zio.{Clock, Random, URLayer, ZIO, ZLayer} +import zio.json.* +import zio.json.ast.Json +import zio.mock.MockSpecDefault +import zio.test.* +import zio.test.Assertion.* + +import java.net.URI +import java.time.Instant +import java.util.UUID +import scala.util.Try + +object OIDCCredentialIssuerServiceSpec + extends MockSpecDefault + with CredentialServiceSpecHelper + with Openid4VCIProofJwtOps { + + val layers: URLayer[ + DIDService & ManagedDIDService & DIDNonSecretStorage & OID4VCIIssuerMetadataService, + CredentialService & CredentialDefinitionService & OIDCCredentialIssuerService + ] = + ZLayer.makeSome[ + DIDService & ManagedDIDService & DIDNonSecretStorage & OID4VCIIssuerMetadataService, + CredentialService & CredentialDefinitionService & OIDCCredentialIssuerService + ]( + InMemoryIssuanceSessionService.layer, + CredentialRepositoryInMemory.layer, + CredentialStatusListRepositoryInMemory.layer, + PrismDidResolver.layer, + ResourceURIDereferencerImpl.layer, + credentialDefinitionServiceLayer, + GenericSecretStorageInMemory.layer, + LinkSecretServiceImpl.layer, + CredentialServiceImpl.layer, + OIDCCredentialIssuerServiceImpl.layer + ) + + override def spec = suite("CredentialServiceImpl")( + oid4vciCredentialIssuerServiceSpec, + validateProofSpec + ) + + private val (_, issuerKp, issuerDidMetadata, issuerDidData) = + MockDIDService.createDID(VerificationRelationship.AssertionMethod) + + private val (holderOp, holderKp, holderDidMetadata, holderDidData) = + MockDIDService.createDID(VerificationRelationship.AssertionMethod) + + private val holderDidServiceExpectations = + MockDIDService.resolveDIDExpectation(holderDidMetadata, holderDidData) + + private val issuerDidServiceExpectations = + MockDIDService.resolveDIDExpectation(issuerDidMetadata, issuerDidData) + + private val issuerManagedDIDServiceExpectations = + MockManagedDIDService.javaKeyPairWithDIDExpectation(issuerKp) + + private val getIssuerPrismDidWalletIdExpectations = + MockDIDNonSecretStorage.getPrismDidWalletIdExpectation(issuerDidData.id, WalletId.default) + + private val getCredentialConfigurationExpectations = + MockOID4VCIIssuerMetadataService.getCredentialConfigurationByIdExpectations( + CredentialConfiguration( + configurationId = "DrivingLicense", + format = CredentialFormat.JWT, + schemaId = URI("resource:///vc-schema-example.json"), + createdAt = Instant.EPOCH + ) + ) + + private def buildJwtProof(nonce: String, aud: UUID, iat: Int) = { + val longFormDid = PrismDID.buildLongFormFromOperation(holderOp) + makeJwtProof(longFormDid, nonce, aud, iat, holderKp.privateKey) + } + + private val validateProofSpec = suite("Validate holder's proof of possession using the LongFormPrismDID")( + test("should validate the holder's proof of possession using the LongFormPrismDID") { + for { + credentialIssuer <- ZIO.service[OIDCCredentialIssuerService] + nonce <- Random.nextString(10) + aud <- Random.nextUUID + iat <- Clock.instant.map(_.getEpochSecond.toInt) + jwt = buildJwtProof(nonce, aud, iat) + result <- credentialIssuer.verifyJwtProof(jwt) + } yield assert(result)(equalTo(true)) + }.provide( + holderDidServiceExpectations.toLayer, + MockManagedDIDService.empty, + MockDIDNonSecretStorage.empty, + MockOID4VCIIssuerMetadataService.empty, + layers + ) + ) + + private val oid4vciCredentialIssuerServiceSpec = + suite("Simple JWT credential issuance")( + test("should issue a JWT credential") { + for { + oidcCredentialIssuerService <- ZIO.service[OIDCCredentialIssuerService] + credentialDefinition = CredentialDefinition( + `@context` = Some(Seq("https://www.w3.org/2018/credentials/v1")), + `type` = Seq("VerifiableCredential", "CertificateOfBirth"), + credentialSubject = Some( + Map( + "name" -> + ClaimDescriptor(mandatory = Some(true), valueType = Some("string"), display = Seq.empty[Localization]) + ) + ) + ) + subjectDID <- ZIO.fromEither(DID.fromString("did:work:MDP8AsFhHzhwUvGNuYkX7T")) + jwt <- oidcCredentialIssuerService + .issueJwtCredential( + issuerDidData.id, + Some(subjectDID), + Json("name" -> Json.Str("Alice")), + None, + credentialDefinition + ) + _ <- zio.Console.printLine(jwt) + jwtObject <- ZIO.fromTry(Try(JWSObject.parse(jwt.value))) + payload <- ZIO.fromEither(Json.decoder.decodeJson(jwtObject.getPayload.toString).flatMap(_.as[Json.Obj])) + vc <- ZIO.fromEither(payload.get("vc").get.as[Json.Obj]) + credentialSubject <- ZIO.fromEither(vc.get("credentialSubject").get.as[Json.Obj]) + name <- ZIO.fromEither(credentialSubject.get("name").get.as[String]) + } yield assert(jwt.value)(isNonEmptyString) && + // assert(jwtObject.getHeader.getKeyID)(equalTo(issuerDidData.id.toString)) && //TODO: add key ID to the header + assert(jwtObject.getHeader.getAlgorithm)(equalTo(JWSAlgorithm.ES256K)) && + assert(name)(equalTo("Alice")) + }.provide( + issuerDidServiceExpectations.toLayer, + issuerManagedDIDServiceExpectations.toLayer, + getIssuerPrismDidWalletIdExpectations.toLayer, + MockOID4VCIIssuerMetadataService.empty, + layers + ), + test("create credential-offer with valid claims") { + val wac = ZLayer.succeed(WalletAccessContext(WalletId.random)) + val claims = Json( + "credentialSubject" -> Json.Obj( + "emailAddress" -> Json.Str("alice@example.com"), + "givenName" -> Json.Str("Alice"), + "familyName" -> Json.Str("Wonderland"), + "dateOfIssuance" -> Json.Str("2000-01-01T10:00:00Z"), + "drivingLicenseID" -> Json.Str("12345"), + "drivingClass" -> Json.Num(5), + ) + ) + for { + oidcCredentialIssuerService <- ZIO.service[OIDCCredentialIssuerService] + offer <- oidcCredentialIssuerService + .createCredentialOffer( + URI("http://example.com").toURL(), + UUID.randomUUID(), + "DrivingLicense", + issuerDidData.id, + claims, + ) + .provide(wac) + issuerState = offer.grants.get.authorization_code.issuer_state.get + session <- oidcCredentialIssuerService.getIssuanceSessionByIssuerState(issuerState) + } yield assert(session.claims)(equalTo(claims)) + }.provide( + MockDIDService.empty, + MockManagedDIDService.empty, + MockDIDNonSecretStorage.empty, + getCredentialConfigurationExpectations.toLayer, + layers + ), + test("reject credential-offer when created with invalid claims") { + val wac = ZLayer.succeed(WalletAccessContext(WalletId.random)) + val claims = Json("credentialSubject" -> Json.Obj("foo" -> Json.Str("bar"))) + for { + oidcCredentialIssuerService <- ZIO.service[OIDCCredentialIssuerService] + exit <- oidcCredentialIssuerService + .createCredentialOffer( + URI("http://example.com").toURL(), + UUID.randomUUID(), + "DrivingLicense", + issuerDidData.id, + claims, + ) + .provide(wac) + .exit + } yield assert(exit)(failsWithA[OIDCCredentialIssuerService.Errors.CredentialSchemaError]) + }.provide( + MockDIDService.empty, + MockManagedDIDService.empty, + MockDIDNonSecretStorage.empty, + getCredentialConfigurationExpectations.toLayer, + layers + ) + ) +} diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/Entity.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/Entity.scala index d8c104895b..51ece93fe4 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/Entity.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/model/Entity.scala @@ -10,6 +10,7 @@ import java.util.UUID enum EntityRole { case Admin extends EntityRole case Tenant extends EntityRole + case ExternalParty extends EntityRole } trait BaseEntity { diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/sql/JdbcDIDNonSecretStorage.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/sql/JdbcDIDNonSecretStorage.scala index 2eb0844711..c2e3f44611 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/sql/JdbcDIDNonSecretStorage.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/sql/JdbcDIDNonSecretStorage.scala @@ -423,6 +423,20 @@ class JdbcDIDNonSecretStorage(xa: Transactor[ContextAwareTask], xb: Transactor[T cnxIO.transact(xb) } + override def getPrismDidWalletId(prismDid: PrismDID): Task[Option[WalletId]] = { + val cnxIO = sql""" + | SELECT + | wallet_id + | FROM public.prism_did_wallet_state + | WHERE + | did = ${prismDid.toString} + """.stripMargin + .query[WalletId] + .option + + cnxIO.transact(xb) + } + } object JdbcDIDNonSecretStorage { diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/DIDNonSecretStorage.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/DIDNonSecretStorage.scala index e8fce8710d..8527cff127 100644 --- a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/DIDNonSecretStorage.scala +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/DIDNonSecretStorage.scala @@ -3,7 +3,7 @@ package org.hyperledger.identus.agent.walletapi.storage import org.hyperledger.identus.agent.walletapi.model.* import org.hyperledger.identus.castor.core.model.did.{PrismDID, ScheduledDIDOperationStatus} import org.hyperledger.identus.mercury.model.DidId -import org.hyperledger.identus.shared.models.WalletAccessContext +import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} import zio.* trait DIDNonSecretStorage { @@ -55,4 +55,6 @@ trait DIDNonSecretStorage { def getPeerDIDRecord(did: DidId): Task[Option[PeerDIDRecord]] + def getPrismDidWalletId(prismDid: PrismDID): Task[Option[WalletId]] + } diff --git a/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/MockDIDNonSecretStorage.scala b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/MockDIDNonSecretStorage.scala new file mode 100644 index 0000000000..004660d9ec --- /dev/null +++ b/cloud-agent/service/wallet-api/src/main/scala/org/hyperledger/identus/agent/walletapi/storage/MockDIDNonSecretStorage.scala @@ -0,0 +1,105 @@ +package org.hyperledger.identus.agent.walletapi.storage + +import org.hyperledger.identus.agent.walletapi.model.* +import org.hyperledger.identus.castor.core.model.did.{PrismDID, ScheduledDIDOperationStatus} +import org.hyperledger.identus.mercury.model.DidId +import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import zio.* +import zio.mock.{Expectation, Mock, Proxy} +import zio.test.Assertion.equalTo + +import scala.collection.immutable.ArraySeq + +case class MockDIDNonSecretStorage(proxy: Proxy) extends DIDNonSecretStorage { + override def insertDIDUpdateLineage(did: PrismDID, updateLineage: DIDUpdateLineage): RIO[WalletAccessContext, Unit] = + proxy(MockDIDNonSecretStorage.InsertDIDUpdateLineage, (did, updateLineage)) + + override def listUpdateLineage( + did: Option[PrismDID], + status: Option[ScheduledDIDOperationStatus] + ): RIO[WalletAccessContext, Seq[DIDUpdateLineage]] = proxy(MockDIDNonSecretStorage.ListUpdateLineage, (did, status)) + + override def setDIDUpdateLineageStatus( + operationId: Array[Byte], + status: ScheduledDIDOperationStatus + ): RIO[WalletAccessContext, Unit] = proxy(MockDIDNonSecretStorage.SetDIDUpdateLineageStatus, (operationId, status)) + + override def createPeerDIDRecord(did: DidId): RIO[WalletAccessContext, Int] = + proxy(MockDIDNonSecretStorage.CreatePeerDIDRecord, did) + + override def getPeerDIDRecord(did: DidId): Task[Option[PeerDIDRecord]] = + proxy(MockDIDNonSecretStorage.GetPeerDIDRecord, did) + + override def getPrismDidWalletId(prismDid: PrismDID): Task[Option[WalletId]] = + proxy(MockDIDNonSecretStorage.GetPrismDidWalletId, prismDid) + + override def getManagedDIDState(did: PrismDID): RIO[WalletAccessContext, Option[ManagedDIDState]] = + proxy(MockDIDNonSecretStorage.GetManagedDIDState, did) + + override def insertManagedDID( + did: PrismDID, + state: ManagedDIDState, + hdKey: Map[String, ManagedDIDHdKeyPath], + randKey: Map[String, ManagedDIDRandKeyMeta] + ): RIO[WalletAccessContext, Unit] = proxy(MockDIDNonSecretStorage.InsertManagedDID, (did, state, hdKey, randKey)) + + override def updateManagedDID(did: PrismDID, patch: ManagedDIDStatePatch): RIO[WalletAccessContext, Unit] = + proxy(MockDIDNonSecretStorage.UpdateManagedDID, (did, patch)) + + override def getMaxDIDIndex(): RIO[WalletAccessContext, Option[Int]] = + proxy(MockDIDNonSecretStorage.GetMaxDIDIndex) + + override def getHdKeyCounter(did: PrismDID): RIO[WalletAccessContext, Option[HdKeyIndexCounter]] = + proxy(MockDIDNonSecretStorage.GetHdKeyCounter, did) + + override def getKeyMeta( + did: PrismDID, + keyId: String + ): RIO[WalletAccessContext, Option[(ManagedDIDKeyMeta, Array[Byte])]] = + proxy(MockDIDNonSecretStorage.GetKeyMeta, (did, keyId)) + + override def insertKeyMeta( + did: PrismDID, + keyId: String, + meta: ManagedDIDKeyMeta, + operationHash: Array[Byte] + ): RIO[WalletAccessContext, Unit] = + proxy(MockDIDNonSecretStorage.InsertHdKeyMeta, (did, keyId, meta, operationHash)) + + override def listManagedDID( + offset: Option[Int], + limit: Option[Int] + ): RIO[WalletAccessContext, (Seq[(PrismDID, ManagedDIDState)], Int)] = + proxy(MockDIDNonSecretStorage.ListManagedDID, (offset, limit)) +} +object MockDIDNonSecretStorage extends Mock[DIDNonSecretStorage] { + object InsertDIDUpdateLineage extends Effect[(PrismDID, DIDUpdateLineage), Throwable, Unit] + object ListUpdateLineage + extends Effect[(Option[PrismDID], Option[ScheduledDIDOperationStatus]), Throwable, Seq[DIDUpdateLineage]] + object SetDIDUpdateLineageStatus extends Effect[(Array[Byte], ScheduledDIDOperationStatus), Throwable, Unit] + object CreatePeerDIDRecord extends Effect[DidId, Throwable, Int] + object GetPeerDIDRecord extends Effect[DidId, Throwable, Option[PeerDIDRecord]] + object GetPrismDidWalletId extends Effect[PrismDID, Throwable, Option[WalletId]] + object GetManagedDIDState extends Effect[PrismDID, Throwable, Option[ManagedDIDState]] + object InsertManagedDID + extends Effect[ + (PrismDID, ManagedDIDState, Map[String, ManagedDIDHdKeyPath], Map[String, ManagedDIDRandKeyMeta]), + Throwable, + Unit + ] + object UpdateManagedDID extends Effect[(PrismDID, ManagedDIDStatePatch), Throwable, Unit] + object GetMaxDIDIndex extends Effect[Unit, Throwable, Option[Int]] + object GetHdKeyCounter extends Effect[PrismDID, Throwable, Option[HdKeyIndexCounter]] + object GetKeyMeta extends Effect[(PrismDID, String), Throwable, Option[(ManagedDIDKeyMeta, Array[Byte])]] + object InsertHdKeyMeta extends Effect[(PrismDID, String, ManagedDIDKeyMeta, Array[Byte]), Throwable, Unit] + object ListHdKeyPath extends Effect[PrismDID, Throwable, Seq[(String, ArraySeq[Byte], ManagedDIDHdKeyPath)]] + object ListManagedDID extends Effect[(Option[Int], Option[Int]), Throwable, (Seq[(PrismDID, ManagedDIDState)], Int)] + + override val compose: URLayer[mock.Proxy, DIDNonSecretStorage] = ZLayer.fromFunction(MockDIDNonSecretStorage(_)) + + def getPrismDidWalletIdExpectation(prismDID: PrismDID, walletId: WalletId): Expectation[DIDNonSecretStorage] = + MockDIDNonSecretStorage.GetPrismDidWalletId( + assertion = equalTo(prismDID), + result = Expectation.value(Some(walletId)) + ) +} diff --git a/examples/.nickel/agent.ncl b/examples/.nickel/agent.ncl new file mode 100644 index 0000000000..1c1a49a124 --- /dev/null +++ b/examples/.nickel/agent.ncl @@ -0,0 +1,134 @@ +let DbConfig = { + host + | String, + port + | Number + | default + = 5432, + dbName + | String, + user + | String, + password + | String, +} +in +let NodeConfig = { + host + | String, + port + | Number + | default + = 50053 +} +in +let KeycloakConfig = { + url + | String, + realmName + | String, + clientId + | String, + clientSecret + | String +} +in +let VaultConfig = { + url + | String, + token + | String, +} +in +let AgentServiceArgs = { + version + | String, + polluxDb + | DbConfig, + connectDb + | DbConfig, + agentDb + | DbConfig, + node + | NodeConfig, + didcommServiceUrl + | String, + restServiceUrl + | String, + apikeyEnabled + | Bool, + keycloak + | KeycloakConfig + | optional, + vault + | VaultConfig + | optional, + bootstrapContainer + | String + | doc "The container that agent must wait to complete before starting" + | optional +} +in +{ + makeAgentService | AgentServiceArgs -> _ + = fun args => + { + image = "ghcr.io/hyperledger/identus-cloud-agent:%{args.version}", + restart = "always", + environment = + { + POLLUX_DB_HOST = args.polluxDb.host, + POLLUX_DB_PORT = std.to_string args.polluxDb.port, + POLLUX_DB_NAME = args.polluxDb.dbName, + POLLUX_DB_USER = args.polluxDb.user, + POLLUX_DB_PASSWORD = args.polluxDb.password, + CONNECT_DB_HOST = args.connectDb.host, + CONNECT_DB_PORT = std.to_string args.connectDb.port, + CONNECT_DB_NAME = args.connectDb.dbName, + CONNECT_DB_USER = args.connectDb.user, + CONNECT_DB_PASSWORD = args.connectDb.password, + AGENT_DB_HOST = args.agentDb.host, + AGENT_DB_PORT = std.to_string args.agentDb.port, + AGENT_DB_NAME = args.agentDb.dbName, + AGENT_DB_USER = args.agentDb.user, + AGENT_DB_PASSWORD = args.agentDb.password, + DIDCOMM_SERVICE_URL = args.didcommServiceUrl, + REST_SERVICE_URL = args.restServiceUrl, + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL = args.restServiceUrl, + PRISM_NODE_HOST = args.node.host, + PRISM_NODE_PORT = std.to_string args.node.port, + ADMIN_TOKEN = "admin", + API_KEY_ENABLED = std.to_string args.apikeyEnabled, + } + & ( + if args |> std.record.has_field "keycloak" then + { + KEYCLOAK_ENABLED = "true", + KEYCLOAK_URL = args.keycloak.url, + KEYCLOAK_REALM = args.keycloak.realmName, + KEYCLOAK_CLIENT_ID = args.keycloak.clientId, + KEYCLOAK_CLIENT_SECRET = args.keycloak.clientSecret + } + else + {} + ) + & ( + if args |> std.record.has_field "vault" then + { + SECRET_STORAGE_BACKEND = "vault", + VAULT_ADDR = args.vault.url, + VAULT_TOKEN = args.vault.token + } + else + { SECRET_STORAGE_BACKEND = "postgres" } + ), + depends_on = + { "%{args.node.host}" = { condition = "service_started" } } + & ( + if args |> std.record.has_field "bootstrapContainer" then + { "%{args.bootstrapContainer}" = { condition = "service_completed_successfully" } } + else + {} + ) + } +} diff --git a/examples/.nickel/bootstrap.ncl b/examples/.nickel/bootstrap.ncl new file mode 100644 index 0000000000..3c9a04b2c1 --- /dev/null +++ b/examples/.nickel/bootstrap.ncl @@ -0,0 +1,19 @@ +let HurlBootstrapServiceArgs = { + version | String, + hurlDir | String, + variables + | { _ : String } + | default + = {} +} +in +{ + makeHurlBootstrapService | HurlBootstrapServiceArgs -> _ + = fun args => + { + image = "ghcr.io/orange-opensource/hurl:%{args.version}", + volumes = ["%{args.hurlDir}:/hurl"], + command = ["--glob", "/hurl/*.hurl", "--test"], + environment = args.variables, + } +} diff --git a/examples/.nickel/build.sh b/examples/.nickel/build.sh new file mode 100755 index 0000000000..7011381a51 --- /dev/null +++ b/examples/.nickel/build.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +find . -name "*.ncl" -print0 | xargs -0 -I _ nickel format _ + +nickel export ./root.ncl -f yaml --field st >../st/compose.yaml +nickel export ./root.ncl -f yaml --field st-vault >../st-vault/compose.yaml +nickel export ./root.ncl -f yaml --field st-multi >../st-multi/compose.yaml +nickel export ./root.ncl -f yaml --field st-oid4vci >../st-oid4vci/compose.yaml + +nickel export ./root.ncl -f yaml --field mt >../mt/compose.yaml +nickel export ./root.ncl -f yaml --field mt-keycloak >../mt-keycloak/compose.yaml +nickel export ./root.ncl -f yaml --field mt-keycloak-vault >../mt-keycloak-vault/compose.yaml diff --git a/examples/.nickel/caddy.ncl b/examples/.nickel/caddy.ncl new file mode 100644 index 0000000000..1f3ccf94b7 --- /dev/null +++ b/examples/.nickel/caddy.ncl @@ -0,0 +1,54 @@ +let CaddyServiceArgs = { + version + | String, + hostPort + | Number, + name + | String + | default + = "default", + agent + | { host | String, restPort | Number, didcommPort | Number }, + keycloak + | { host | String, port | Number }, + vault + | { host | String, port | Number }, +} +in +{ + makeCaddyConfig | CaddyServiceArgs -> _ + = fun args => + { + "caddyfile_%{args.name}" = { + content = m%" + :%{std.to_string args.hostPort} { + handle_path /didcomm* { + reverse_proxy %{args.agent.host}:%{std.to_string args.agent.didcommPort} + } + handle_path /prism-agent* { + reverse_proxy %{args.agent.host}:%{std.to_string args.agent.restPort} + } + handle_path /keycloak* { + reverse_proxy %{args.keycloak.host}:%{std.to_string args.keycloak.port} + } + handle_path /vault* { + reverse_proxy %{args.vault.host}:%{std.to_string args.vault.port} + } + } + "% + } + }, + makeCaddyService | CaddyServiceArgs -> _ + = fun args => + { + image = "caddy:%{args.version}", + restart = "always", + configs = [ + { + source = "caddyfile_%{args.name}", + target = "/etc/caddy/Caddyfile" + } + ], + ports = ["%{std.to_string args.hostPort}:%{std.to_string args.hostPort}"], + } +} diff --git a/examples/.nickel/db.ncl b/examples/.nickel/db.ncl new file mode 100644 index 0000000000..1df83ebab4 --- /dev/null +++ b/examples/.nickel/db.ncl @@ -0,0 +1,33 @@ +let DbServiceArgs = { + version + | String, + databases + | String, + volumeName + | String +} +in +{ + makeDbService | DbServiceArgs -> _ + = fun args => + { + image = "postgres:%{args.version}", + restart = "always", + environment = { + POSTGRES_MULTIPLE_DATABASES = args.databases, + POSTGRES_USER = "postgres", + POSTGRES_PASSWORD = "postgres", + }, + volumes = [ + "%{args.volumeName}:/var/lib/postgresql/data", + "../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh", + "../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql", + ], + healthcheck = { + test = ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"], + interval = "10s", + timeout = "5s", + retries = 5 + } + } +} diff --git a/examples/.nickel/keycloak.ncl b/examples/.nickel/keycloak.ncl new file mode 100644 index 0000000000..83297b6bfc --- /dev/null +++ b/examples/.nickel/keycloak.ncl @@ -0,0 +1,34 @@ +let KeycloakServiceArgs = { + hostname + | String + | default + = "localhost", + hostnamePath + | String + | default + = "/keycloak", + hostnamePort + | Number +} +in +{ + makeKeycloakService + | KeycloakServiceArgs + & { version | String } -> _ + = fun args => + { + image = "quay.io/keycloak/keycloak:%{args.version}", + restart = "always", + environment = { + KEYCLOAK_ADMIN = "admin", + KEYCLOAK_ADMIN_PASSWORD = "admin", + }, + command = [ + "start-dev", + "--features=preview", + "--health-enabled=true", + "--hostname-url=http://%{args.hostname}:%{std.to_string args.hostnamePort}%{args.hostnamePath}", + "--hostname-admin-url=http://%{args.hostname}:%{std.to_string args.hostnamePort}%{args.hostnamePath}", + ] + } +} diff --git a/examples/.nickel/node.ncl b/examples/.nickel/node.ncl new file mode 100644 index 0000000000..62110c50b3 --- /dev/null +++ b/examples/.nickel/node.ncl @@ -0,0 +1,41 @@ +let DbConfig = { + host + | String, + port + | Number + | default + = 5432, + dbName + | String, + user + | String, + password + | String, +} +in +let NodeServiceArgs = { + version + | String, + db + | DbConfig, +} +in +{ + makeNodeService | NodeServiceArgs -> _ + = fun args => + { + image = "ghcr.io/input-output-hk/prism-node:%{args.version}", + restart = "always", + environment = { + NODE_PSQL_HOST = "%{args.db.host}:%{std.to_string args.db.port}", + NODE_PSQL_DATABASE = args.db.dbName, + NODE_PSQL_USERNAME = args.db.user, + NODE_PSQL_PASSWORD = args.db.password, + }, + depends_on = { + "%{args.db.host}" = { + condition = "service_healthy" + } + } + } +} diff --git a/examples/.nickel/root.ncl b/examples/.nickel/root.ncl new file mode 100644 index 0000000000..1276ba261b --- /dev/null +++ b/examples/.nickel/root.ncl @@ -0,0 +1,42 @@ +let stack = import "./stack.ncl" +in +{ + st = + stack.makeAgentStack { name = "issuer", port = 8080 }, + + st-vault = + stack.makeAgentStack { name = "issuer", port = 8080, vault.hostPort = 8200 }, + + st-multi = + (stack.makeAgentStack { name = "issuer", port = 8080 }) + & (stack.makeAgentStack { name = "holder", port = 8081 }) + & (stack.makeAgentStack { name = "verifier", port = 8082 }), + + st-oid4vci = + (stack.makeAgentStack { name = "issuer", port = 8080 }) + & (stack.makeMockServerStack { port = 7777 }) + & ( + stack.makeIssuerKeycloakStack + { + name = "issuer", + port = 9980, + realm = "students", + extraEnvs = { IDENTUS_URL = "http://caddy-issuer:8080/prism-agent" } + } + ), + + mt = + stack.makeAgentStack { name = "default", port = 8080, apikeyEnabled = true }, + + mt-keycloak = + stack.makeAgentStack { name = "default", port = 8080, keycloakEnabled = true }, + + mt-keycloak-vault = + stack.makeAgentStack + { + name = "default", + port = 8080, + keycloakEnabled = true, + vault.hostPort = 8200, + }, +} diff --git a/examples/.nickel/stack.ncl b/examples/.nickel/stack.ncl new file mode 100644 index 0000000000..a7e713a090 --- /dev/null +++ b/examples/.nickel/stack.ncl @@ -0,0 +1,230 @@ +let _agent = import "./agent.ncl" in +let _db = import "./db.ncl" in +let _node = import "./node.ncl" in +let _caddy = import "./caddy.ncl" in +let _keycloak = import "./keycloak.ncl" in +let _bootstrap = import "./bootstrap.ncl" in +let _vault = import "./vault.ncl" in +let V = import "./versions.ncl" in +let ExternalKeycloakStackConfig = { + name | String, + port | Number, + realm | String, + extraEnvs + | { _ | String } + | default + = {} +} +in +let AgentStackConfig = { + name | String, + port | Number, + apikeyEnabled | Bool | default = false, + keycloakEnabled | Bool | default = false, + vault | { hostPort | Number } | optional, +} +in +{ + makeMockServerStack | { port | Number } -> _ + = fun args => + { + services = { + mockserver = { + image = "mockserver/mockserver:%{V.mockServer}", + ports = ["%{std.to_string args.port}:1080"] + } + } + }, + makeIssuerKeycloakStack | ExternalKeycloakStackConfig -> _ + = fun args => + { + services = { + "external-keycloak-%{args.name}" = + ( + _keycloak.makeKeycloakService + { + version = V.keycloak, + hostnamePath = "", + hostnamePort = args.port, + } + |> std.record.remove "image" + ) + & { + image = "ghcr.io/hyperledger/identus-keycloak-plugins:%{V.identusKeycloak}", + ports = ["%{std.to_string args.port}:8080"], + environment = args.extraEnvs + }, + "external-keycloak-init-%{args.name}" = + _bootstrap.makeHurlBootstrapService + { + version = V.hurl, + hurlDir = "./bootstrap", + variables = { + HURL_keycloak_base_url = "http://external-keycloak-%{args.name}:8080", + HURL_keycloak_admin_user = "admin", + HURL_keycloak_admin_password = "admin", + HURL_keycloak_realm = args.realm, + HURL_alice_username = "alice", + HURL_alice_password = "1234", + HURL_alice_wallet_client_id = "alice-wallet", + HURL_agent_client_id = "cloud-agent", + HURL_agent_client_secret = "secret", + } + }, + } + }, + makeAgentStack | AgentStackConfig -> _ + = fun args => + let pgDockerVolumeName = "pg_data_%{args.name}" in + let hosts = { + caddy = "caddy-%{args.name}", + agent = "agent-%{args.name}", + keycloak = "keycloak-%{args.name}", + keycloakInit = "keycloak-init-%{args.name}", + vault = "vault-%{args.name}", + db = "db-%{args.name}", + # node services are static so they are the same when merged between stacks + node = "node", + nodeDb = "node-db", + } + in + let caddyArgs = { + version = V.caddy, + hostPort = args.port, + name = args.name, + agent = { + host = hosts.agent, + restPort = 8085, + didcommPort = 8090, + }, + keycloak = { + host = hosts.keycloak, + port = 8080 + }, + vault = { + host = hosts.vault, + port = 8200 + } + } + in + let makeSharedDbConfig = fun databaseName => + { + host = hosts.db, + dbName = databaseName, + user = "postgres", + password = "postgres", + } + in + { + configs = _caddy.makeCaddyConfig caddyArgs, + volumes = { + pg_data_node = {}, + "%{pgDockerVolumeName}" = {}, + }, + services = + { + "%{hosts.caddy}" = _caddy.makeCaddyService caddyArgs, + "%{hosts.nodeDb}" = + _db.makeDbService + { + version = V.postgres, + databases = "node_db", + volumeName = "pg_data_node" + }, + "%{hosts.db}" = + _db.makeDbService + { + version = V.postgres, + databases = "pollux,connect,agent", + volumeName = pgDockerVolumeName + }, + "%{hosts.node}" = + _node.makeNodeService + { + version = V.node, + db = { + host = hosts.nodeDb, + dbName = "node_db", + user = "postgres", + password = "postgres", + }, + }, + "%{hosts.agent}" = + _agent.makeAgentService + ( + { + version = V.agent, + polluxDb = makeSharedDbConfig "pollux", + connectDb = makeSharedDbConfig "connect", + agentDb = makeSharedDbConfig "agent", + node = { host = "node" }, + didcommServiceUrl = "http://%{hosts.caddy}:%{std.to_string args.port}/didcomm", + restServiceUrl = "http://%{hosts.caddy}:%{std.to_string args.port}/prism-agent", + apikeyEnabled = args.apikeyEnabled, + } + & ( + if args.keycloakEnabled then + { + keycloak = { + url = "http://%{hosts.keycloak}:8080", + realmName = "identus", + clientId = "agent", + clientSecret = "agent-secret" + }, + bootstrapContainer = "%{hosts.keycloakInit}", + } + else + {} + ) + & ( + if args |> std.record.has_field "vault" then + { vault = { url = "http://%{hosts.vault}:8200", token = "admin" } } + else + {} + ) + ), + } + # optional services + & ( + if args.keycloakEnabled then + { + "%{hosts.keycloak}" = + _keycloak.makeKeycloakService + { + version = V.keycloak, + hostnamePort = args.port + }, + "%{hosts.keycloakInit}" = + _bootstrap.makeHurlBootstrapService + { + version = V.hurl, + hurlDir = "../.shared/hurl/simple_realm", + variables = { + HURL_KEYCLOAK_BASE_URL = "http://%{hosts.keycloak}:8080", + HURL_KEYCLOAK_ADMIN_USER = "admin", + HURL_KEYCLOAK_ADMIN_PASSWORD = "admin", + HURL_KEYCLOAK_REALM = "identus", + HURL_KEYCLOAK_CLIENT_ID = "agent", + HURL_KEYCLOAK_CLIENT_SECRET = "agent-secret", + } + }, + } + else + {} + ) + & ( + if args |> std.record.has_field "vault" then + { + "%{hosts.vault}" = + _vault.makeVaultService + { + version = V.vault, + hostPort = args.vault.hostPort, + rootToken = "admin" + } + } + else + {} + ) + } +} diff --git a/examples/.nickel/vault.ncl b/examples/.nickel/vault.ncl new file mode 100644 index 0000000000..3b0514748b --- /dev/null +++ b/examples/.nickel/vault.ncl @@ -0,0 +1,30 @@ +let VaultServiceArgs = { + version + | String, + hostPort + | Number, + rootToken + | String + | default + = "admin" +} +in +{ + makeVaultService | VaultServiceArgs -> _ + = fun args => + { + image = "hashicorp/vault:%{args.version}", + ports = ["%{std.to_string args.hostPort}:8200"], + environment = { + VAULT_ADDR = "http://0.0.0.0:8200", + VAULT_DEV_ROOT_TOKEN_ID = args.rootToken + }, + cap_add = ["IPC_LOCK"], + healthcheck = { + test = ["CMD", "vault", "status"], + interval = "10s", + timeout = "5s", + retries = "5", + } + } +} diff --git a/examples/.nickel/versions.ncl b/examples/.nickel/versions.ncl new file mode 100644 index 0000000000..5f6ee3f616 --- /dev/null +++ b/examples/.nickel/versions.ncl @@ -0,0 +1,13 @@ +{ + # identus + agent = "1.36.1-SNAPSHOT", + node = "2.4.0", + identusKeycloak = "0.1.0", + # 3rd party + caddy = "2.7.6-alpine", + mockServer = "5.15.0", + keycloak = "23.0.7", + hurl = "4.2.0", + postgres = "13", + vault = "1.15.6", +} diff --git a/examples/.shared/hurl/simple_realm/01_init_realm.hurl b/examples/.shared/hurl/simple_realm/01_init_realm.hurl new file mode 100644 index 0000000000..ff3b4c86c6 --- /dev/null +++ b/examples/.shared/hurl/simple_realm/01_init_realm.hurl @@ -0,0 +1,47 @@ +# Wait for keycloak ready +GET {{ keycloak_base_url }}/health/ready +[Options] +retry: 300 +HTTP 200 + +# Admin login +POST {{ keycloak_base_url }}/realms/master/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: admin-cli +username: {{ keycloak_admin_user }} +password: {{ keycloak_admin_password }} +HTTP 200 +[Captures] +admin_access_token: jsonpath "$.access_token" + +# Create realm +POST {{ keycloak_base_url }}/admin/realms +authorization: Bearer {{ admin_access_token }} +{ + "realm": "{{ keycloak_realm }}", + "enabled": true +} +HTTP 201 + +# Create agent client +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/clients +Authorization: Bearer {{ admin_access_token }} +{ + "id": "{{ keycloak_client_id }}", + "authorizationServicesEnabled": true, + "serviceAccountsEnabled": true, + "secret": "{{ keycloak_client_secret }}" +} +HTTP 201 + +# Create frontend client +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/clients +Authorization: Bearer {{ admin_access_token }} +{ + "id": "web-ui", + "directAccessGrantsEnabled": true, + "publicClient": true +} +HTTP 201 + diff --git a/examples/.shared/postgres/init-script.sh b/examples/.shared/postgres/init-script.sh new file mode 100755 index 0000000000..58c7cd1034 --- /dev/null +++ b/examples/.shared/postgres/init-script.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e +set -u + +function create_user_and_database() { + local database=$1 + local app_user=${database}-application-user + echo " Creating user and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE USER "$app_user" WITH PASSWORD 'password'; + CREATE DATABASE $database; + \c $database + ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "$app_user"; + EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo "$POSTGRES_MULTIPLE_DATABASES" | tr ',' ' '); do + create_user_and_database "$db" + done + echo "Multiple databases created" +fi diff --git a/examples/.shared/postgres/max_conns.sql b/examples/.shared/postgres/max_conns.sql new file mode 100644 index 0000000000..f2a343e505 --- /dev/null +++ b/examples/.shared/postgres/max_conns.sql @@ -0,0 +1 @@ +ALTER SYSTEM SET max_connections = 500; diff --git a/examples/aries-cloud-agent.json b/examples/aries-cloud-agent.json deleted file mode 100644 index 4804124daa..0000000000 --- a/examples/aries-cloud-agent.json +++ /dev/null @@ -1,11244 +0,0 @@ -{ - "swagger" : "2.0", - "info" : { - "version" : "v1.0.0-rc0", - "title" : "Aries Cloud Agent" - }, - "tags" : [ { - "name" : "action-menu", - "description" : "Menu interaction over connection" - }, { - "name" : "basicmessage", - "description" : "Simple messaging", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/527849ec3aa2a8fd47a7bb6c57f918ff8bcb5e8c/features/0095-basic-message" - } - }, { - "name" : "connection", - "description" : "Connection management", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/9b0aaa39df7e8bd434126c4b33c097aae78d65bf/features/0160-connection-protocol" - } - }, { - "name" : "credential-definition", - "description" : "Credential definition operations", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/indy-node/blob/master/design/anoncreds.md#cred_def" - } - }, { - "name" : "credentials", - "description" : "Holder credential management", - "externalDocs" : { - "description" : "Overview", - "url" : "https://w3c.github.io/vc-data-model/#credentials" - } - }, { - "name" : "did-exchange", - "description" : "Connection management via DID exchange", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/25464a5c8f8a17b14edaa4310393df6094ace7b0/features/0023-did-exchange" - } - }, { - "name" : "endorse-transaction", - "description" : "Endorse a Transaction" - }, { - "name" : "introduction", - "description" : "Introduction of known parties" - }, { - "name" : "issue-credential v1.0", - "description" : "Credential issue v1.0", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/bb42a6c35e0d5543718fb36dd099551ab192f7b0/features/0036-issue-credential" - } - }, { - "name" : "issue-credential v2.0", - "description" : "Credential issue v2.0", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/cd27fc64aa2805f756a118043d7c880354353047/features/0453-issue-credential-v2" - } - }, { - "name" : "jsonld", - "description" : "Sign and verify json-ld data", - "externalDocs" : { - "description" : "Specification", - "url" : "https://tools.ietf.org/html/rfc7515" - } - }, { - "name" : "ledger", - "description" : "Interaction with ledger", - "externalDocs" : { - "description" : "Overview", - "url" : "https://hyperledger-indy.readthedocs.io/projects/plenum/en/latest/storage.html#ledger" - } - }, { - "name" : "mediation", - "description" : "Mediation management", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/fa8dc4ea1e667eb07db8f9ffeaf074a4455697c0/features/0211-route-coordination" - } - }, { - "name" : "multitenancy", - "description" : "Multitenant wallet management" - }, { - "name" : "out-of-band", - "description" : "Out-of-band connections", - "externalDocs" : { - "description" : "Design", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/2da7fc4ee043effa3a9960150e7ba8c9a4628b68/features/0434-outofband" - } - }, { - "name" : "present-proof v1.0", - "description" : "Proof presentation v1.0", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/4fae574c03f9f1013db30bf2c0c676b1122f7149/features/0037-present-proof" - } - }, { - "name" : "present-proof v2.0", - "description" : "Proof presentation v2.0", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/eace815c3e8598d4a8dd7881d8c731fdb2bcc0aa/features/0454-present-proof-v2" - } - }, { - "name" : "resolver", - "description" : "did resolver interface.", - "externalDocs" : { - "description" : "Specification" - } - }, { - "name" : "revocation", - "description" : "Revocation registry management", - "externalDocs" : { - "description" : "Overview", - "url" : "https://github.com/hyperledger/indy-hipe/tree/master/text/0011-cred-revocation" - } - }, { - "name" : "schema", - "description" : "Schema operations", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/indy-node/blob/master/design/anoncreds.md#schema" - } - }, { - "name" : "server", - "description" : "Feature discovery", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/9b7ab9814f2e7d1108f74aca6f3d2e5d62899473/features/0031-discover-features" - } - }, { - "name" : "trustping", - "description" : "Trust-ping over connection", - "externalDocs" : { - "description" : "Specification", - "url" : "https://github.com/hyperledger/aries-rfcs/tree/527849ec3aa2a8fd47a7bb6c57f918ff8bcb5e8c/features/0048-trust-ping" - } - }, { - "name" : "wallet", - "description" : "DID and tag policy management", - "externalDocs" : { - "description" : "Design", - "url" : "https://github.com/hyperledger/indy-sdk/tree/master/docs/design/003-wallet-storage" - } - } ], - "security" : [ { - "AuthorizationHeader" : [ ] - } ], - "paths" : { - "/action-menu/{conn_id}/close" : { - "post" : { - "tags" : [ "action-menu" ], - "summary" : "Close the active menu associated with a connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } - } - } - } - }, - "/action-menu/{conn_id}/fetch" : { - "post" : { - "tags" : [ "action-menu" ], - "summary" : "Fetch the active menu", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuFetchResult" - } - } - } - } - }, - "/action-menu/{conn_id}/perform" : { - "post" : { - "tags" : [ "action-menu" ], - "summary" : "Perform an action associated with the active menu", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/PerformRequest" - } - }, { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } - } - } - } - }, - "/action-menu/{conn_id}/request" : { - "post" : { - "tags" : [ "action-menu" ], - "summary" : "Request the active menu", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } - } - } - } - }, - "/action-menu/{conn_id}/send-menu" : { - "post" : { - "tags" : [ "action-menu" ], - "summary" : "Send an action menu to a connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SendMenu" - } - }, { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ActionMenuModulesResult" - } - } - } - } - }, - "/connections" : { - "get" : { - "tags" : [ "connection" ], - "summary" : "Query agent-to-agent connections", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "alias", - "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" - }, { - "name" : "connection_protocol", - "in" : "query", - "description" : "Connection protocol used", - "required" : false, - "type" : "string", - "enum" : [ "connections/1.0", "didexchange/1.0" ] - }, { - "name" : "invitation_key", - "in" : "query", - "description" : "invitation key", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, { - "name" : "my_did", - "in" : "query", - "description" : "My DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "state", - "in" : "query", - "description" : "Connection state", - "required" : false, - "type" : "string", - "enum" : [ "start", "abandoned", "active", "completed", "response", "init", "invitation", "error", "request" ] - }, { - "name" : "their_did", - "in" : "query", - "description" : "Their DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "their_role", - "in" : "query", - "description" : "Their role in the connection protocol", - "required" : false, - "type" : "string", - "enum" : [ "invitee", "requester", "inviter", "responder" ] - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionList" - } - } - } - } - }, - "/connections/create-invitation" : { - "post" : { - "tags" : [ "connection" ], - "summary" : "Create a new connection invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/CreateInvitationRequest" - } - }, { - "name" : "alias", - "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" - }, { - "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" - }, { - "name" : "multi_use", - "in" : "query", - "description" : "Create invitation for multiple use (default false)", - "required" : false, - "type" : "boolean" - }, { - "name" : "public", - "in" : "query", - "description" : "Create invitation from public DID (default false)", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/InvitationResult" - } - } - } - } - }, - "/connections/create-static" : { - "post" : { - "tags" : [ "connection" ], - "summary" : "Create a new static connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ConnectionStaticRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionStaticResult" - } - } - } - } - }, - "/connections/receive-invitation" : { - "post" : { - "tags" : [ "connection" ], - "summary" : "Receive a new connection invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ReceiveInvitationRequest" - } - }, { - "name" : "alias", - "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" - }, { - "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" - }, { - "name" : "mediation_id", - "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/connections/{conn_id}" : { - "get" : { - "tags" : [ "connection" ], - "summary" : "Fetch a single connection record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - }, - "delete" : { - "tags" : [ "connection" ], - "summary" : "Remove an existing connection record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionModuleResponse" - } - } - } - } - }, - "/connections/{conn_id}/accept-invitation" : { - "post" : { - "tags" : [ "connection" ], - "summary" : "Accept a stored connection invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "mediation_id", - "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", - "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, { - "name" : "my_label", - "in" : "query", - "description" : "Label for connection", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/connections/{conn_id}/accept-request" : { - "post" : { - "tags" : [ "connection" ], - "summary" : "Accept a stored connection request", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "my_endpoint", - "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/connections/{conn_id}/endpoints" : { - "get" : { - "tags" : [ "connection" ], - "summary" : "Fetch connection remote endpoint", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/EndpointsResult" - } - } - } - } - }, - "/connections/{conn_id}/establish-inbound/{ref_id}" : { - "post" : { - "tags" : [ "connection" ], - "summary" : "Assign another connection as the inbound connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "ref_id", - "in" : "path", - "description" : "Inbound connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionModuleResponse" - } - } - } - } - }, - "/connections/{conn_id}/metadata" : { - "get" : { - "tags" : [ "connection" ], - "summary" : "Fetch connection metadata", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "key", - "in" : "query", - "description" : "Key to retrieve.", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionMetadata" - } - } - } - }, - "post" : { - "tags" : [ "connection" ], - "summary" : "Set connection metadata", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ConnectionMetadataSetRequest" - } - }, { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnectionMetadata" - } - } - } - } - }, - "/connections/{conn_id}/send-message" : { - "post" : { - "tags" : [ "basicmessage" ], - "summary" : "Send a basic message to a connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SendMessage" - } - }, { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/BasicMessageModuleResponse" - } - } - } - } - }, - "/connections/{conn_id}/send-ping" : { - "post" : { - "tags" : [ "trustping" ], - "summary" : "Send a trust ping to a connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/PingRequest" - } - }, { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/PingRequestResponse" - } - } - } - } - }, - "/connections/{conn_id}/start-introduction" : { - "post" : { - "tags" : [ "introduction" ], - "summary" : "Start an introduction between two connections", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "target_connection_id", - "in" : "query", - "description" : "Target connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "message", - "in" : "query", - "description" : "Message", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IntroModuleResponse" - } - } - } - } - }, - "/credential-definitions" : { - "post" : { - "tags" : [ "credential-definition" ], - "summary" : "Sends a credential definition to the ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionSendRequest" - } - }, { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrCredentialDefinitionSendResult" - } - } - } - } - }, - "/credential-definitions/created" : { - "get" : { - "tags" : [ "credential-definition" ], - "summary" : "Search for matching credential definitions that agent originated", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_def_id", - "in" : "query", - "description" : "Credential definition id", - "required" : false, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, { - "name" : "issuer_did", - "in" : "query", - "description" : "Issuer DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "schema_id", - "in" : "query", - "description" : "Schema identifier", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, { - "name" : "schema_issuer_did", - "in" : "query", - "description" : "Schema issuer DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "schema_name", - "in" : "query", - "description" : "Schema name", - "required" : false, - "type" : "string" - }, { - "name" : "schema_version", - "in" : "query", - "description" : "Schema version", - "required" : false, - "type" : "string", - "pattern" : "^[0-9.]+$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionsCreatedResult" - } - } - } - } - }, - "/credential-definitions/{cred_def_id}" : { - "get" : { - "tags" : [ "credential-definition" ], - "summary" : "Gets a credential definition from the ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_def_id", - "in" : "path", - "description" : "Credential definition identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionGetResult" - } - } - } - } - }, - "/credential-definitions/{cred_def_id}/write_record" : { - "post" : { - "tags" : [ "credential-definition" ], - "summary" : "Writes a credential definition non-secret record to the wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_def_id", - "in" : "path", - "description" : "Credential definition identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredentialDefinitionGetResult" - } - } - } - } - }, - "/credential/mime-types/{credential_id}" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Get attribute MIME types from wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "credential_id", - "in" : "path", - "description" : "Credential identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AttributeMimeTypesResult" - } - } - } - } - }, - "/credential/revoked/{credential_id}" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Query credential revocation status by id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "credential_id", - "in" : "path", - "description" : "Credential identifier", - "required" : true, - "type" : "string" - }, { - "name" : "from", - "in" : "query", - "description" : "Earliest epoch of revocation status interval of interest", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - }, { - "name" : "to", - "in" : "query", - "description" : "Latest epoch of revocation status interval of interest", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredRevokedResult" - } - } - } - } - }, - "/credential/w3c/{credential_id}" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Fetch W3C credential from wallet by id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "credential_id", - "in" : "path", - "description" : "Credential identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/VCRecord" - } - } - } - }, - "delete" : { - "tags" : [ "credentials" ], - "summary" : "Remove W3C credential from wallet by id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "credential_id", - "in" : "path", - "description" : "Credential identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/HolderModuleResponse" - } - } - } - } - }, - "/credential/{credential_id}" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Fetch credential from wallet by id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "credential_id", - "in" : "path", - "description" : "Credential identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IndyCredInfo" - } - } - } - }, - "delete" : { - "tags" : [ "credentials" ], - "summary" : "Remove credential from wallet by id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "credential_id", - "in" : "path", - "description" : "Credential identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/HolderModuleResponse" - } - } - } - } - }, - "/credentials" : { - "get" : { - "tags" : [ "credentials" ], - "summary" : "Fetch credentials from wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "count", - "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - }, { - "name" : "wql", - "in" : "query", - "description" : "(JSON) WQL query", - "required" : false, - "type" : "string", - "pattern" : "^{.*}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredInfoList" - } - } - } - } - }, - "/credentials/w3c" : { - "post" : { - "tags" : [ "credentials" ], - "summary" : "Fetch W3C credentials from wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/W3CCredentialsListRequest" - } - }, { - "name" : "count", - "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - }, { - "name" : "wql", - "in" : "query", - "description" : "(JSON) WQL query", - "required" : false, - "type" : "string", - "pattern" : "^{.*}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/VCRecordList" - } - } - } - } - }, - "/didexchange/create-request" : { - "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Create and send a request against public DID's implicit invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "their_public_did", - "in" : "query", - "description" : "Qualified public DID to which to request connection", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$|^did:([a-zA-Z0-9_]+):([a-zA-Z0-9_.%-]+(:[a-zA-Z0-9_.%-]+)*)((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\\/[^#?]*)?([?][^#]*)?(\\#.*)?$$" - }, { - "name" : "mediation_id", - "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", - "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, { - "name" : "my_label", - "in" : "query", - "description" : "Label for connection request", - "required" : false, - "type" : "string" - }, { - "name" : "use_public_did", - "in" : "query", - "description" : "Use public DID for this connection", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/didexchange/receive-request" : { - "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Receive request against public DID's implicit invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/DIDXRequest" - } - }, { - "name" : "alias", - "in" : "query", - "description" : "Alias for connection", - "required" : false, - "type" : "string" - }, { - "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" - }, { - "name" : "mediation_id", - "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", - "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/didexchange/{conn_id}/accept-invitation" : { - "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Accept a stored connection invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "my_endpoint", - "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, { - "name" : "my_label", - "in" : "query", - "description" : "Label for connection request", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/didexchange/{conn_id}/accept-request" : { - "post" : { - "tags" : [ "did-exchange" ], - "summary" : "Accept a stored connection request", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "mediation_id", - "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "my_endpoint", - "in" : "query", - "description" : "My URL endpoint", - "required" : false, - "type" : "string", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/features" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Query supported features", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "query", - "in" : "query", - "description" : "Query", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/QueryResult" - } - } - } - } - }, - "/issue-credential-2.0/create" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Create credential from attribute values", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20IssueCredSchemaCore" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/create-offer" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Create a credential offer, independent of any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredOfferConnFreeRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/records" : { - "get" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Fetch all credential exchange records", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "connection_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", - "in" : "query", - "description" : "Role assigned in credential exchange", - "required" : false, - "type" : "string", - "enum" : [ "issuer", "holder" ] - }, { - "name" : "state", - "in" : "query", - "description" : "Credential exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done" ] - }, { - "name" : "thread_id", - "in" : "query", - "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordListResult" - } - } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}" : { - "get" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Fetch a single credential exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } - } - } - }, - "delete" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Remove an existing credential exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20IssueCredentialModuleResponse" - } - } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/issue" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredIssueRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } - } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/problem-report" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send a problem report for credential exchange", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredIssueProblemReportRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20IssueCredentialModuleResponse" - } - } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/send-offer" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential offer in reference to a proposal with preview", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/send-request" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send issuer a credential request", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredRequestRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/records/{cred_ex_id}/store" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Store a received credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredStoreRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } - } - } - } - }, - "/issue-credential-2.0/send" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential, automating entire flow", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredExFree" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/send-offer" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send holder a credential offer, independent of any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredOfferRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/send-proposal" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send issuer a credential proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredExFree" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential-2.0/send-request" : { - "post" : { - "tags" : [ "issue-credential v2.0" ], - "summary" : "Send issuer a credential request not bound to an existing thread. Indy credentials cannot start at a request", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20CredRequestFree" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20CredExRecord" - } - } - } - } - }, - "/issue-credential/create" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential, automating entire flow", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialCreate" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/create-offer" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Create a credential offer, independent of any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialConnFreeOfferRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/records" : { - "get" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Fetch all credential exchange records", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "connection_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", - "in" : "query", - "description" : "Role assigned in credential exchange", - "required" : false, - "type" : "string", - "enum" : [ "issuer", "holder" ] - }, { - "name" : "state", - "in" : "query", - "description" : "Credential exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal_sent", "proposal_received", "offer_sent", "offer_received", "request_sent", "request_received", "credential_issued", "credential_received", "credential_acked" ] - }, { - "name" : "thread_id", - "in" : "query", - "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchangeListResult" - } - } - } - } - }, - "/issue-credential/records/{cred_ex_id}" : { - "get" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Fetch a single credential exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - }, - "delete" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Remove an existing credential exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IssueCredentialModuleResponse" - } - } - } - } - }, - "/issue-credential/records/{cred_ex_id}/issue" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialIssueRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/records/{cred_ex_id}/problem-report" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send a problem report for credential exchange", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialProblemReportRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/IssueCredentialModuleResponse" - } - } - } - } - }, - "/issue-credential/records/{cred_ex_id}/send-offer" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential offer in reference to a proposal with preview", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialBoundOfferRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/records/{cred_ex_id}/send-request" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send issuer a credential request", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/records/{cred_ex_id}/store" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Store a received credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialStoreRequest" - } - }, { - "name" : "cred_ex_id", - "in" : "path", - "description" : "Credential exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/send" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential, automating entire flow", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialProposalRequestMand" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/send-offer" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send holder a credential offer, independent of any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialFreeOfferRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/issue-credential/send-proposal" : { - "post" : { - "tags" : [ "issue-credential v1.0" ], - "summary" : "Send issuer a credential proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10CredentialProposalRequestOpt" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - } - }, - "/jsonld/sign" : { - "post" : { - "tags" : [ "jsonld" ], - "summary" : "Sign a JSON-LD structure and return it", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SignRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SignResponse" - } - } - } - } - }, - "/jsonld/verify" : { - "post" : { - "tags" : [ "jsonld" ], - "summary" : "Verify a JSON-LD structure.", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/VerifyRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/VerifyResponse" - } - } - } - } - }, - "/ledger/did-endpoint" : { - "get" : { - "tags" : [ "ledger" ], - "summary" : "Get the endpoint for a DID from the ledger.", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "endpoint_type", - "in" : "query", - "description" : "Endpoint type of interest (default 'Endpoint')", - "required" : false, - "type" : "string", - "enum" : [ "Endpoint", "Profile", "LinkedDomains" ] - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/GetDIDEndpointResponse" - } - } - } - } - }, - "/ledger/did-verkey" : { - "get" : { - "tags" : [ "ledger" ], - "summary" : "Get the verkey for a DID from the ledger.", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/GetDIDVerkeyResponse" - } - } - } - } - }, - "/ledger/get-nym-role" : { - "get" : { - "tags" : [ "ledger" ], - "summary" : "Get the role from the NYM registration of a public DID.", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/GetNymRoleResponse" - } - } - } - } - }, - "/ledger/register-nym" : { - "post" : { - "tags" : [ "ledger" ], - "summary" : "Send a NYM registration to the ledger.", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID to register", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "verkey", - "in" : "query", - "description" : "Verification key", - "required" : true, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, { - "name" : "alias", - "in" : "query", - "description" : "Alias", - "required" : false, - "type" : "string" - }, { - "name" : "role", - "in" : "query", - "description" : "Role", - "required" : false, - "type" : "string", - "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "reset" ] - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RegisterLedgerNymResponse" - } - } - } - } - }, - "/ledger/rotate-public-did-keypair" : { - "patch" : { - "tags" : [ "ledger" ], - "summary" : "Rotate key pair for public DID.", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/LedgerModulesResult" - } - } - } - } - }, - "/ledger/taa" : { - "get" : { - "tags" : [ "ledger" ], - "summary" : "Fetch the current transaction author agreement, if any", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TAAResult" - } - } - } - } - }, - "/ledger/taa/accept" : { - "post" : { - "tags" : [ "ledger" ], - "summary" : "Accept the transaction author agreement", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/TAAAccept" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/LedgerModulesResult" - } - } - } - } - }, - "/mediation/default-mediator" : { - "get" : { - "tags" : [ "mediation" ], - "summary" : "Get default mediator", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - }, - "delete" : { - "tags" : [ "mediation" ], - "summary" : "Clear default mediator", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - } - }, - "/mediation/keylists" : { - "get" : { - "tags" : [ "mediation" ], - "summary" : "Retrieve keylists by connection or role", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier (optional)", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", - "in" : "query", - "description" : "Filer on role, 'client' for keys mediated by other agents, 'server' for keys mediated by this agent", - "required" : false, - "type" : "string", - "default" : "server", - "enum" : [ "client", "server" ] - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/Keylist" - } - } - } - } - }, - "/mediation/keylists/{mediation_id}/send-keylist-query" : { - "post" : { - "tags" : [ "mediation" ], - "summary" : "Send keylist query to mediator", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/KeylistQueryFilterRequest" - } - }, { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - }, { - "name" : "paginate_limit", - "in" : "query", - "description" : "limit number of results", - "required" : false, - "type" : "integer", - "default" : -1, - "format" : "int32" - }, { - "name" : "paginate_offset", - "in" : "query", - "description" : "offset to use in pagination", - "required" : false, - "type" : "integer", - "default" : 0, - "format" : "int32" - } ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/KeylistQuery" - } - } - } - } - }, - "/mediation/keylists/{mediation_id}/send-keylist-update" : { - "post" : { - "tags" : [ "mediation" ], - "summary" : "Send keylist update to mediator", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/KeylistUpdateRequest" - } - }, { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/KeylistUpdate" - } - } - } - } - }, - "/mediation/request/{conn_id}" : { - "post" : { - "tags" : [ "mediation" ], - "summary" : "Request mediation from connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/MediationCreateRequest" - } - }, { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - } - }, - "/mediation/requests" : { - "get" : { - "tags" : [ "mediation" ], - "summary" : "Query mediation requests, returns list of all mediation records", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier (optional)", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "mediator_terms", - "in" : "query", - "description" : "List of mediator rules for recipient", - "required" : false, - "type" : "array", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the mediator requires the recipient to agree" - }, - "collectionFormat" : "multi" - }, { - "name" : "recipient_terms", - "in" : "query", - "description" : "List of recipient rules for mediation", - "required" : false, - "type" : "array", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the recipient requires the mediator to agree" - }, - "collectionFormat" : "multi" - }, { - "name" : "state", - "in" : "query", - "description" : "Mediation state (optional)", - "required" : false, - "type" : "string", - "enum" : [ "request", "granted", "denied" ] - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationList" - } - } - } - } - }, - "/mediation/requests/{mediation_id}" : { - "get" : { - "tags" : [ "mediation" ], - "summary" : "Retrieve mediation request record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - }, - "delete" : { - "tags" : [ "mediation" ], - "summary" : "Delete mediation request by ID", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - } - }, - "/mediation/requests/{mediation_id}/deny" : { - "post" : { - "tags" : [ "mediation" ], - "summary" : "Deny a stored mediation request", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/AdminMediationDeny" - } - }, { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationDeny" - } - } - } - } - }, - "/mediation/requests/{mediation_id}/grant" : { - "post" : { - "tags" : [ "mediation" ], - "summary" : "Grant received mediation", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationGrant" - } - } - } - } - }, - "/mediation/{mediation_id}/default-mediator" : { - "put" : { - "tags" : [ "mediation" ], - "summary" : "Set default mediator", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "mediation_id", - "in" : "path", - "description" : "Mediation record identifier", - "required" : true, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "201" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - } - }, - "/multitenancy/wallet" : { - "post" : { - "tags" : [ "multitenancy" ], - "summary" : "Create a subwallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/CreateWalletRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CreateWalletResponse" - } - } - } - } - }, - "/multitenancy/wallet/{wallet_id}" : { - "get" : { - "tags" : [ "multitenancy" ], - "summary" : "Get a single subwallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "wallet_id", - "in" : "path", - "description" : "Subwallet identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletRecord" - } - } - } - }, - "put" : { - "tags" : [ "multitenancy" ], - "summary" : "Update a subwallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/UpdateWalletRequest" - } - }, { - "name" : "wallet_id", - "in" : "path", - "description" : "Subwallet identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletRecord" - } - } - } - } - }, - "/multitenancy/wallet/{wallet_id}/remove" : { - "post" : { - "tags" : [ "multitenancy" ], - "summary" : "Remove a subwallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RemoveWalletRequest" - } - }, { - "name" : "wallet_id", - "in" : "path", - "description" : "Subwallet identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/MultitenantModuleResponse" - } - } - } - } - }, - "/multitenancy/wallet/{wallet_id}/token" : { - "post" : { - "tags" : [ "multitenancy" ], - "summary" : "Get auth token for a subwallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/CreateWalletTokenRequest" - } - }, { - "name" : "wallet_id", - "in" : "path", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CreateWalletTokenResponse" - } - } - } - } - }, - "/multitenancy/wallets" : { - "get" : { - "tags" : [ "multitenancy" ], - "summary" : "Query subwallets", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "wallet_name", - "in" : "query", - "description" : "Wallet name", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletList" - } - } - } - } - }, - "/out-of-band/create-invitation" : { - "post" : { - "tags" : [ "out-of-band" ], - "summary" : "Create a new connection invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/InvitationCreateRequest" - } - }, { - "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" - }, { - "name" : "multi_use", - "in" : "query", - "description" : "Create invitation for multiple use (default false)", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/InvitationRecord" - } - } - } - } - }, - "/out-of-band/receive-invitation" : { - "post" : { - "tags" : [ "out-of-band" ], - "summary" : "Receive a new connection invitation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/InvitationMessage" - } - }, { - "name" : "alias", - "in" : "query", - "description" : "Alias for connection", - "required" : false, - "type" : "string" - }, { - "name" : "auto_accept", - "in" : "query", - "description" : "Auto-accept connection (defaults to configuration)", - "required" : false, - "type" : "boolean" - }, { - "name" : "mediation_id", - "in" : "query", - "description" : "Identifier for active mediation record to be used", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "use_existing_connection", - "in" : "query", - "description" : "Use an existing connection, if possible", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - } - }, - "/plugins" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Fetch the list of loaded plugins", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminModules" - } - } - } - } - }, - "/present-proof-2.0/create-request" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Creates a presentation request not bound to any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresCreateRequestRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - } - }, - "/present-proof-2.0/records" : { - "get" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Fetch all present-proof exchange records", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "connection_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", - "in" : "query", - "description" : "Role assigned in presentation exchange", - "required" : false, - "type" : "string", - "enum" : [ "prover", "verifier" ] - }, { - "name" : "state", - "in" : "query", - "description" : "Presentation exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned" ] - }, { - "name" : "thread_id", - "in" : "query", - "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecordList" - } - } - } - } - }, - "/present-proof-2.0/records/{pres_ex_id}" : { - "get" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Fetch a single presentation exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - }, - "delete" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Remove an existing presentation exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresentProofModuleResponse" - } - } - } - } - }, - "/present-proof-2.0/records/{pres_ex_id}/credentials" : { - "get" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Fetch credentials from wallet for presentation request", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "count", - "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "extra_query", - "in" : "query", - "description" : "(JSON) object mapping referents to extra WQL queries", - "required" : false, - "type" : "string", - "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" - }, { - "name" : "referent", - "in" : "query", - "description" : "Proof request referents of interest, comma-separated", - "required" : false, - "type" : "string" - }, { - "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyCredPrecis" - } - } - } - } - } - }, - "/present-proof-2.0/records/{pres_ex_id}/problem-report" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Send a problem report for presentation exchange", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresProblemReportRequest" - } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresentProofModuleResponse" - } - } - } - } - }, - "/present-proof-2.0/records/{pres_ex_id}/send-presentation" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a proof presentation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresSpecByFormatRequest" - } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - } - }, - "/present-proof-2.0/records/{pres_ex_id}/send-request" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a presentation request in reference to a proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/AdminAPIMessageTracing" - } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - } - }, - "/present-proof-2.0/records/{pres_ex_id}/verify-presentation" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Verify a received presentation", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - } - }, - "/present-proof-2.0/send-proposal" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a presentation proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresProposalRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - } - }, - "/present-proof-2.0/send-request" : { - "post" : { - "tags" : [ "present-proof v2.0" ], - "summary" : "Sends a free presentation request not bound to any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V20PresSendRequestRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - } - }, - "/present-proof/create-request" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Creates a presentation request not bound to any proposal or connection", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationCreateRequestRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - } - }, - "/present-proof/records" : { - "get" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Fetch all present-proof exchange records", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "connection_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - }, { - "name" : "role", - "in" : "query", - "description" : "Role assigned in presentation exchange", - "required" : false, - "type" : "string", - "enum" : [ "prover", "verifier" ] - }, { - "name" : "state", - "in" : "query", - "description" : "Presentation exchange state", - "required" : false, - "type" : "string", - "enum" : [ "proposal_sent", "proposal_received", "request_sent", "request_received", "presentation_sent", "presentation_received", "verified", "presentation_acked" ] - }, { - "name" : "thread_id", - "in" : "query", - "description" : "Thread identifier", - "required" : false, - "type" : "string", - "format" : "uuid" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchangeList" - } - } - } - } - }, - "/present-proof/records/{pres_ex_id}" : { - "get" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Fetch a single presentation exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - }, - "delete" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Remove an existing presentation exchange record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentProofModuleResponse" - } - } - } - } - }, - "/present-proof/records/{pres_ex_id}/credentials" : { - "get" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Fetch credentials for a presentation request from wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "count", - "in" : "query", - "description" : "Maximum number to retrieve", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "extra_query", - "in" : "query", - "description" : "(JSON) object mapping referents to extra WQL queries", - "required" : false, - "type" : "string", - "pattern" : "^{\\s*\".*?\"\\s*:\\s*{.*?}\\s*(,\\s*\".*?\"\\s*:\\s*{.*?}\\s*)*\\s*}$" - }, { - "name" : "referent", - "in" : "query", - "description" : "Proof request referents of interest, comma-separated", - "required" : false, - "type" : "string" - }, { - "name" : "start", - "in" : "query", - "description" : "Start index", - "required" : false, - "type" : "string", - "pattern" : "^[0-9]*$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyCredPrecis" - } - } - } - } - } - }, - "/present-proof/records/{pres_ex_id}/problem-report" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Send a problem report for presentation exchange", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationProblemReportRequest" - } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentProofModuleResponse" - } - } - } - } - }, - "/present-proof/records/{pres_ex_id}/send-presentation" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a proof presentation", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/IndyPresSpec" - } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - } - }, - "/present-proof/records/{pres_ex_id}/send-request" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a presentation request in reference to a proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/AdminAPIMessageTracing" - } - }, { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - } - }, - "/present-proof/records/{pres_ex_id}/verify-presentation" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Verify a received presentation", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "pres_ex_id", - "in" : "path", - "description" : "Presentation exchange identifier", - "required" : true, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - } - }, - "/present-proof/send-proposal" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a presentation proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationProposalRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - } - }, - "/present-proof/send-request" : { - "post" : { - "tags" : [ "present-proof v1.0" ], - "summary" : "Sends a free presentation request not bound to any proposal", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/V10PresentationSendRequestRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - } - }, - "/resolver/resolve/{did}" : { - "get" : { - "tags" : [ "resolver" ], - "summary" : "Retrieve doc for requested did", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "path", - "description" : "DID", - "required" : true, - "type" : "string", - "pattern" : "^did:([a-z0-9]+):((?:[a-zA-Z0-9._%-]*:)*[a-zA-Z0-9._%-]+)$" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/ResolutionResult" - } - } - } - } - }, - "/revocation/active-registry/{cred_def_id}" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Get current active revocation registry by credential definition id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_def_id", - "in" : "path", - "description" : "Credential definition identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } - } - } - } - }, - "/revocation/clear-pending-revocations" : { - "post" : { - "tags" : [ "revocation" ], - "summary" : "Clear pending revocations", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/ClearPendingRevocationsRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/PublishRevocations" - } - } - } - } - }, - "/revocation/create-registry" : { - "post" : { - "tags" : [ "revocation" ], - "summary" : "Creates a new revocation registry", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RevRegCreateRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } - } - } - } - }, - "/revocation/credential-record" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Get credential revocation status", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_ex_id", - "in" : "query", - "description" : "Credential exchange identifier", - "required" : false, - "type" : "string", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, { - "name" : "cred_rev_id", - "in" : "query", - "description" : "Credential revocation identifier", - "required" : false, - "type" : "string", - "pattern" : "^[1-9][0-9]*$" - }, { - "name" : "rev_reg_id", - "in" : "query", - "description" : "Revocation registry identifier", - "required" : false, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/CredRevRecordResult" - } - } - } - } - }, - "/revocation/publish-revocations" : { - "post" : { - "tags" : [ "revocation" ], - "summary" : "Publish pending revocations to ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/PublishRevocations" - } - }, { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrPublishRevocationsResult" - } - } - } - } - }, - "/revocation/registries/created" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Search for matching revocation registries that current agent created", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "cred_def_id", - "in" : "query", - "description" : "Credential definition identifier", - "required" : false, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, { - "name" : "state", - "in" : "query", - "description" : "Revocation registry state", - "required" : false, - "type" : "string", - "enum" : [ "init", "generated", "posted", "active", "full" ] - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegsCreated" - } - } - } - } - }, - "/revocation/registry/{rev_reg_id}" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Get revocation registry by revocation registry id", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } - } - } - }, - "patch" : { - "tags" : [ "revocation" ], - "summary" : "Update revocation registry with new public URI to its tails file", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RevRegUpdateTailsFileUri" - } - }, { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } - } - } - } - }, - "/revocation/registry/{rev_reg_id}/definition" : { - "post" : { - "tags" : [ "revocation" ], - "summary" : "Send revocation registry definition to ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrRevRegResult" - } - } - } - } - }, - "/revocation/registry/{rev_reg_id}/entry" : { - "post" : { - "tags" : [ "revocation" ], - "summary" : "Send revocation registry entry to ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } - } - } - } - }, - "/revocation/registry/{rev_reg_id}/issued" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Get number of credentials issued against revocation registry", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegIssuedResult" - } - } - } - } - }, - "/revocation/registry/{rev_reg_id}/set-state" : { - "patch" : { - "tags" : [ "revocation" ], - "summary" : "Set revocation registry state manually", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, { - "name" : "state", - "in" : "query", - "description" : "Revocation registry state to set", - "required" : true, - "type" : "string", - "enum" : [ "init", "generated", "posted", "active", "full" ] - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevRegResult" - } - } - } - } - }, - "/revocation/registry/{rev_reg_id}/tails-file" : { - "get" : { - "tags" : [ "revocation" ], - "summary" : "Download tails file", - "produces" : [ "application/octet-stream" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } ], - "responses" : { - "200" : { - "description" : "tails file", - "schema" : { - "type" : "string", - "format" : "binary" - } - } - } - }, - "put" : { - "tags" : [ "revocation" ], - "summary" : "Upload local tails file to server", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "rev_reg_id", - "in" : "path", - "description" : "Revocation Registry identifier", - "required" : true, - "type" : "string", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevocationModuleResponse" - } - } - } - } - }, - "/revocation/revoke" : { - "post" : { - "tags" : [ "revocation" ], - "summary" : "Revoke an issued credential", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/RevokeRequest" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/RevocationModuleResponse" - } - } - } - } - }, - "/schemas" : { - "post" : { - "tags" : [ "schema" ], - "summary" : "Sends a schema to the ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/SchemaSendRequest" - } - }, { - "name" : "conn_id", - "in" : "query", - "description" : "Connection identifier", - "required" : false, - "type" : "string" - }, { - "name" : "create_transaction_for_endorser", - "in" : "query", - "description" : "Create Transaction For Endorser's signature", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/TxnOrSchemaSendResult" - } - } - } - } - }, - "/schemas/created" : { - "get" : { - "tags" : [ "schema" ], - "summary" : "Search for matching schema that agent originated", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "schema_id", - "in" : "query", - "description" : "Schema identifier", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, { - "name" : "schema_issuer_did", - "in" : "query", - "description" : "Schema issuer DID", - "required" : false, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "schema_name", - "in" : "query", - "description" : "Schema name", - "required" : false, - "type" : "string" - }, { - "name" : "schema_version", - "in" : "query", - "description" : "Schema version", - "required" : false, - "type" : "string", - "pattern" : "^[0-9.]+$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SchemasCreatedResult" - } - } - } - } - }, - "/schemas/{schema_id}" : { - "get" : { - "tags" : [ "schema" ], - "summary" : "Gets a schema from the ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "schema_id", - "in" : "path", - "description" : "Schema identifier", - "required" : true, - "type" : "string", - "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SchemaGetResult" - } - } - } - } - }, - "/schemas/{schema_id}/write_record" : { - "post" : { - "tags" : [ "schema" ], - "summary" : "Writes a schema non-secret record to the wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "schema_id", - "in" : "path", - "description" : "Schema identifier", - "required" : true, - "type" : "string", - "pattern" : "^[1-9][0-9]*|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/SchemaGetResult" - } - } - } - } - }, - "/shutdown" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Shut down server", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminShutdown" - } - } - } - } - }, - "/status" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Fetch the server status", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminStatus" - } - } - } - } - }, - "/status/config" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Fetch the server configuration", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminConfig" - } - } - } - } - }, - "/status/live" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Liveliness check", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminStatusLiveliness" - } - } - } - } - }, - "/status/ready" : { - "get" : { - "tags" : [ "server" ], - "summary" : "Readiness check", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminStatusReadiness" - } - } - } - } - }, - "/status/reset" : { - "post" : { - "tags" : [ "server" ], - "summary" : "Reset statistics", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/AdminReset" - } - } - } - } - }, - "/transaction/{tran_id}/resend" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Author to resend a particular transaction request", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "tran_id", - "in" : "path", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/transactions" : { - "get" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Query transactions", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionList" - } - } - } - } - }, - "/transactions/create-request" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For author to send a transaction request", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/Date" - } - }, { - "name" : "tran_id", - "in" : "query", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - }, { - "name" : "endorser_write_txn", - "in" : "query", - "description" : "Endorser will write the transaction after endorsing it", - "required" : false, - "type" : "boolean" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/transactions/{conn_id}/set-endorser-info" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Set Endorser Info", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "endorser_did", - "in" : "query", - "description" : "Endorser DID", - "required" : true, - "type" : "string" - }, { - "name" : "endorser_name", - "in" : "query", - "description" : "Endorser Name", - "required" : false, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/EndorserInfo" - } - } - } - } - }, - "/transactions/{conn_id}/set-endorser-role" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Set transaction jobs", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "conn_id", - "in" : "path", - "description" : "Connection identifier", - "required" : true, - "type" : "string" - }, { - "name" : "transaction_my_job", - "in" : "query", - "description" : "Transaction related jobs", - "required" : false, - "type" : "string", - "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionJobs" - } - } - } - } - }, - "/transactions/{tran_id}" : { - "get" : { - "tags" : [ "endorse-transaction" ], - "summary" : "Fetch a single transaction record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "tran_id", - "in" : "path", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/transactions/{tran_id}/cancel" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Author to cancel a particular transaction request", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "tran_id", - "in" : "path", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/transactions/{tran_id}/endorse" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Endorser to endorse a particular transaction record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "tran_id", - "in" : "path", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/transactions/{tran_id}/refuse" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Endorser to refuse a particular transaction record", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "tran_id", - "in" : "path", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/transactions/{tran_id}/write" : { - "post" : { - "tags" : [ "endorse-transaction" ], - "summary" : "For Author / Endorser to write an endorsed transaction to the ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "tran_id", - "in" : "path", - "description" : "Transaction identifier", - "required" : true, - "type" : "string" - } ], - "responses" : { - "200" : { - "description" : "null", - "schema" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - } - }, - "/wallet/did" : { - "get" : { - "tags" : [ "wallet" ], - "summary" : "List wallet DIDs", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : false, - "type" : "string", - "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$|^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, { - "name" : "key_type", - "in" : "query", - "description" : "Key type to query for.", - "required" : false, - "type" : "string", - "enum" : [ "ed25519", "bls12381g2" ] - }, { - "name" : "method", - "in" : "query", - "description" : "DID method to query for. e.g. sov to only fetch indy/sov DIDs", - "required" : false, - "type" : "string", - "enum" : [ "key", "sov" ] - }, { - "name" : "posture", - "in" : "query", - "description" : "Whether DID is current public DID, posted to ledger but current public DID, or local to the wallet", - "required" : false, - "type" : "string", - "enum" : [ "public", "posted", "wallet_only" ] - }, { - "name" : "verkey", - "in" : "query", - "description" : "Verification key of interest", - "required" : false, - "type" : "string", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDList" - } - } - } - } - }, - "/wallet/did/create" : { - "post" : { - "tags" : [ "wallet" ], - "summary" : "Create a local DID", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/DIDCreate" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDResult" - } - } - } - } - }, - "/wallet/did/local/rotate-keypair" : { - "patch" : { - "tags" : [ "wallet" ], - "summary" : "Rotate keypair for a DID not posted to the ledger", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletModuleResponse" - } - } - } - } - }, - "/wallet/did/public" : { - "get" : { - "tags" : [ "wallet" ], - "summary" : "Fetch the current public DID", - "produces" : [ "application/json" ], - "parameters" : [ ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDResult" - } - } - } - }, - "post" : { - "tags" : [ "wallet" ], - "summary" : "Assign the current public DID", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDResult" - } - } - } - } - }, - "/wallet/get-did-endpoint" : { - "get" : { - "tags" : [ "wallet" ], - "summary" : "Query DID endpoint in wallet", - "produces" : [ "application/json" ], - "parameters" : [ { - "name" : "did", - "in" : "query", - "description" : "DID of interest", - "required" : true, - "type" : "string", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/DIDEndpoint" - } - } - } - } - }, - "/wallet/set-did-endpoint" : { - "post" : { - "tags" : [ "wallet" ], - "summary" : "Update endpoint in wallet and on ledger if posted to it", - "produces" : [ "application/json" ], - "parameters" : [ { - "in" : "body", - "name" : "body", - "required" : false, - "schema" : { - "$ref" : "#/definitions/DIDEndpointWithType" - } - } ], - "responses" : { - "200" : { - "description" : "", - "schema" : { - "$ref" : "#/definitions/WalletModuleResponse" - } - } - } - } - } - }, - "securityDefinitions" : { - "AuthorizationHeader" : { - "description" : "Bearer token. Be sure to preprend token with 'Bearer '", - "type" : "apiKey", - "name" : "Authorization", - "in" : "header" - } - }, - "definitions" : { - "AMLRecord" : { - "type" : "object", - "properties" : { - "aml" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - }, - "amlContext" : { - "type" : "string" - }, - "version" : { - "type" : "string" - } - } - }, - "ActionMenuFetchResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/ActionMenuFetchResult_result" - } - } - }, - "ActionMenuModulesResult" : { - "type" : "object" - }, - "AdminAPIMessageTracing" : { - "type" : "object", - "properties" : { - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "AdminConfig" : { - "type" : "object", - "properties" : { - "config" : { - "type" : "object", - "description" : "Configuration settings", - "properties" : { } - } - } - }, - "AdminMediationDeny" : { - "type" : "object", - "properties" : { - "mediator_terms" : { - "type" : "array", - "description" : "List of mediator rules for recipient", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the mediator requires the recipient to agree" - } - }, - "recipient_terms" : { - "type" : "array", - "description" : "List of recipient rules for mediation", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the recipient requires the mediator to agree" - } - } - } - }, - "AdminModules" : { - "type" : "object", - "properties" : { - "result" : { - "type" : "array", - "description" : "List of admin modules", - "items" : { - "type" : "string", - "description" : "admin module" - } - } - } - }, - "AdminReset" : { - "type" : "object" - }, - "AdminShutdown" : { - "type" : "object" - }, - "AdminStatus" : { - "type" : "object", - "properties" : { - "conductor" : { - "type" : "object", - "description" : "Conductor statistics", - "properties" : { } - }, - "label" : { - "type" : "string", - "description" : "Default label", - "x-nullable" : true - }, - "timing" : { - "type" : "object", - "description" : "Timing results", - "properties" : { } - }, - "version" : { - "type" : "string", - "description" : "Version code" - } - } - }, - "AdminStatusLiveliness" : { - "type" : "object", - "properties" : { - "alive" : { - "type" : "boolean", - "example" : true, - "description" : "Liveliness status" - } - } - }, - "AdminStatusReadiness" : { - "type" : "object", - "properties" : { - "ready" : { - "type" : "boolean", - "example" : true, - "description" : "Readiness status" - } - } - }, - "AttachDecorator" : { - "type" : "object", - "required" : [ "data" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Attachment identifier" - }, - "byte_count" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Byte count of data included by reference" - }, - "data" : { - "$ref" : "#/definitions/AttachDecoratorData" - }, - "description" : { - "type" : "string", - "example" : "view from doorway, facing east, with lights off", - "description" : "Human-readable description of content" - }, - "filename" : { - "type" : "string", - "example" : "IMG1092348.png", - "description" : "File name" - }, - "lastmod_time" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Hint regarding last modification datetime, in ISO-8601 format", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "mime-type" : { - "type" : "string", - "example" : "image/png", - "description" : "MIME type" - } - } - }, - "AttachDecoratorData" : { - "type" : "object", - "properties" : { - "base64" : { - "type" : "string", - "example" : "ey4uLn0=", - "description" : "Base64-encoded data", - "pattern" : "^[a-zA-Z0-9+/]*={0,2}$" - }, - "json" : { - "type" : "object", - "example" : "{\"sample\": \"content\"}", - "description" : "JSON-serialized data", - "properties" : { } - }, - "jws" : { - "$ref" : "#/definitions/AttachDecoratorData_jws" - }, - "links" : { - "type" : "array", - "description" : "List of hypertext links to data", - "items" : { - "type" : "string", - "example" : "https://link.to/data" - } - }, - "sha256" : { - "type" : "string", - "example" : "617a48c7c8afe0521efdc03e5bb0ad9e655893e6b4b51f0e794d70fba132aacb", - "description" : "SHA256 hash (binhex encoded) of content", - "pattern" : "^[a-fA-F0-9+/]{64}$" - } - } - }, - "AttachDecoratorData1JWS" : { - "type" : "object", - "required" : [ "header", "signature" ], - "properties" : { - "header" : { - "$ref" : "#/definitions/AttachDecoratorDataJWSHeader" - }, - "protected" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "protected JWS header", - "pattern" : "^[-_a-zA-Z0-9]*$" - }, - "signature" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "signature", - "pattern" : "^[-_a-zA-Z0-9]*$" - } - } - }, - "AttachDecoratorDataJWS" : { - "type" : "object", - "properties" : { - "header" : { - "$ref" : "#/definitions/AttachDecoratorDataJWSHeader" - }, - "protected" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "protected JWS header", - "pattern" : "^[-_a-zA-Z0-9]*$" - }, - "signature" : { - "type" : "string", - "example" : "ey4uLn0", - "description" : "signature", - "pattern" : "^[-_a-zA-Z0-9]*$" - }, - "signatures" : { - "type" : "array", - "description" : "List of signatures", - "items" : { - "$ref" : "#/definitions/AttachDecoratorData1JWS" - } - } - } - }, - "AttachDecoratorDataJWSHeader" : { - "type" : "object", - "required" : [ "kid" ], - "properties" : { - "kid" : { - "type" : "string", - "example" : "did:sov:LjgpST2rjsoxYegQDRm7EL#keys-4", - "description" : "Key identifier, in W3C did:key or DID URL format", - "pattern" : "^did:(?:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+|sov:[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}(;.*)?(\\?.*)?#.+)$" - } - } - }, - "AttachmentDef" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string", - "example" : "attachment-0", - "description" : "Attachment identifier" - }, - "type" : { - "type" : "string", - "example" : "present-proof", - "description" : "Attachment type", - "enum" : [ "credential-offer", "present-proof" ] - } - } - }, - "AttributeMimeTypesResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "description" : "MIME type" - }, - "x-nullable" : true - } - } - }, - "BasicMessageModuleResponse" : { - "type" : "object" - }, - "ClaimFormat" : { - "type" : "object", - "properties" : { - "jwt" : { - "type" : "object", - "properties" : { } - }, - "jwt_vc" : { - "type" : "object", - "properties" : { } - }, - "jwt_vp" : { - "type" : "object", - "properties" : { } - }, - "ldp" : { - "type" : "object", - "properties" : { } - }, - "ldp_vc" : { - "type" : "object", - "properties" : { } - }, - "ldp_vp" : { - "type" : "object", - "properties" : { } - } - } - }, - "ClearPendingRevocationsRequest" : { - "type" : "object", - "properties" : { - "purge" : { - "type" : "object", - "description" : "Credential revocation ids by revocation registry id: omit for all, specify null or empty list for all pending per revocation registry", - "additionalProperties" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - } - } - } - } - }, - "ConnRecord" : { - "type" : "object", - "properties" : { - "accept" : { - "type" : "string", - "example" : "auto", - "description" : "Connection acceptance: manual or auto", - "enum" : [ "manual", "auto" ] - }, - "alias" : { - "type" : "string", - "example" : "Bob, providing quotes", - "description" : "Optional alias to apply to connection for later use" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "connection_protocol" : { - "type" : "string", - "example" : "connections/1.0", - "description" : "Connection protocol used", - "enum" : [ "connections/1.0", "didexchange/1.0" ] - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "error_msg" : { - "type" : "string", - "example" : "No DIDDoc provided; cannot connect to public DID", - "description" : "Error message" - }, - "inbound_connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Inbound routing connection id to use" - }, - "invitation_key" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Public key for connection", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "invitation_mode" : { - "type" : "string", - "example" : "once", - "description" : "Invitation mode", - "enum" : [ "once", "multi", "static" ] - }, - "invitation_msg_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "ID of out-of-band invitation message" - }, - "my_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Our DID for connection", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "request_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection request identifier" - }, - "rfc23_state" : { - "type" : "string", - "example" : "invitation-sent", - "description" : "State per RFC 23", - "readOnly" : true - }, - "routing_state" : { - "type" : "string", - "example" : "active", - "description" : "Routing state of connection", - "enum" : [ "none", "request", "active", "error" ] - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "their_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Their DID for connection", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "their_label" : { - "type" : "string", - "example" : "Bob", - "description" : "Their label for connection" - }, - "their_public_did" : { - "type" : "string", - "example" : "2cpBmR3FqGKWi5EyUbpRY8", - "description" : "Other agent's public DID for connection" - }, - "their_role" : { - "type" : "string", - "example" : "requester", - "description" : "Their role in the connection protocol", - "enum" : [ "invitee", "requester", "inviter", "responder" ] - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "ConnectionInvitation" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID for connection invitation", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "imageUrl" : { - "type" : "string", - "format" : "url", - "example" : "http://192.168.56.101/img/logo.jpg", - "description" : "Optional image URL for connection invitation", - "x-nullable" : true - }, - "label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label for connection invitation" - }, - "recipientKeys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Recipient public key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "routingKeys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Routing key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "serviceEndpoint" : { - "type" : "string", - "example" : "http://192.168.56.101:8020", - "description" : "Service endpoint at which to reach this agent" - } - } - }, - "ConnectionList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of connection records", - "items" : { - "$ref" : "#/definitions/ConnRecord" - } - } - } - }, - "ConnectionMetadata" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "object", - "description" : "Dictionary of metadata associated with connection.", - "properties" : { } - } - } - }, - "ConnectionMetadataSetRequest" : { - "type" : "object", - "required" : [ "metadata" ], - "properties" : { - "metadata" : { - "type" : "object", - "description" : "Dictionary of metadata to set for connection.", - "properties" : { } - } - } - }, - "ConnectionModuleResponse" : { - "type" : "object" - }, - "ConnectionStaticRequest" : { - "type" : "object", - "properties" : { - "alias" : { - "type" : "string", - "description" : "Alias to assign to this connection" - }, - "my_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Local DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "my_seed" : { - "type" : "string", - "description" : "Seed to use for the local DID" - }, - "their_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Remote DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "their_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "URL endpoint for other party", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "their_label" : { - "type" : "string", - "description" : "Other party's label for this connection" - }, - "their_seed" : { - "type" : "string", - "description" : "Seed to use for the remote DID" - }, - "their_verkey" : { - "type" : "string", - "description" : "Remote verification key" - } - } - }, - "ConnectionStaticResult" : { - "type" : "object", - "required" : [ "mv_verkey", "my_did", "my_endpoint", "record", "their_did", "their_verkey" ], - "properties" : { - "mv_verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "My verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "my_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Local DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "my_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "My URL endpoint", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "record" : { - "$ref" : "#/definitions/ConnRecord" - }, - "their_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Remote DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "their_verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Remote verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - } - }, - "Constraints" : { - "type" : "object", - "properties" : { - "fields" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/DIFField" - } - }, - "is_holder" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/DIFHolder" - } - }, - "limit_disclosure" : { - "type" : "string", - "description" : "LimitDisclosure" - }, - "status_active" : { - "type" : "string", - "enum" : [ "required", "allowed", "disallowed" ] - }, - "status_revoked" : { - "type" : "string", - "enum" : [ "required", "allowed", "disallowed" ] - }, - "status_suspended" : { - "type" : "string", - "enum" : [ "required", "allowed", "disallowed" ] - }, - "subject_is_issuer" : { - "type" : "string", - "description" : "SubjectIsIssuer", - "enum" : [ "required", "preferred" ] - } - } - }, - "CreateInvitationRequest" : { - "type" : "object", - "properties" : { - "mediation_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Identifier for active mediation record to be used", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "metadata" : { - "type" : "object", - "description" : "Optional metadata to attach to the connection created with the invitation", - "properties" : { } - }, - "my_label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label for connection invitation" - }, - "recipient_keys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Recipient public key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "routing_keys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Routing key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "service_endpoint" : { - "type" : "string", - "example" : "http://192.168.56.102:8020", - "description" : "Connection endpoint" - } - } - }, - "CreateWalletRequest" : { - "type" : "object", - "properties" : { - "image_url" : { - "type" : "string", - "example" : "https://aries.ca/images/sample.png", - "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection." - }, - "key_management_mode" : { - "type" : "string", - "example" : "managed", - "description" : "Key management method to use for this wallet.", - "enum" : [ "managed" ] - }, - "label" : { - "type" : "string", - "example" : "Alice", - "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection." - }, - "wallet_dispatch_type" : { - "type" : "string", - "example" : "default", - "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", - "enum" : [ "default", "both", "base" ] - }, - "wallet_key" : { - "type" : "string", - "example" : "MySecretKey123", - "description" : "Master key used for key derivation." - }, - "wallet_name" : { - "type" : "string", - "example" : "MyNewWallet", - "description" : "Wallet name" - }, - "wallet_type" : { - "type" : "string", - "example" : "indy", - "description" : "Type of the wallet to create", - "enum" : [ "askar", "in_memory", "indy" ] - }, - "wallet_webhook_urls" : { - "type" : "array", - "description" : "List of Webhook URLs associated with this subwallet", - "items" : { - "type" : "string", - "example" : "http://localhost:8022/webhooks", - "description" : "Optional webhook URL to receive webhook messages" - } - } - } - }, - "CreateWalletResponse" : { - "type" : "object", - "required" : [ "key_management_mode", "wallet_id" ], - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "key_management_mode" : { - "type" : "string", - "description" : "Mode regarding management of wallet key", - "enum" : [ "managed", "unmanaged" ] - }, - "settings" : { - "type" : "object", - "description" : "Settings for this wallet.", - "properties" : { } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "token" : { - "type" : "string", - "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", - "description" : "Authorization token to authenticate wallet requests" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "wallet_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet record ID" - } - } - }, - "CreateWalletTokenRequest" : { - "type" : "object", - "properties" : { - "wallet_key" : { - "type" : "string", - "example" : "MySecretKey123", - "description" : "Master key used for key derivation. Only required for unamanged wallets." - } - } - }, - "CreateWalletTokenResponse" : { - "type" : "object", - "properties" : { - "token" : { - "type" : "string", - "example" : "eyJhbGciOiJFZERTQSJ9.eyJhIjogIjAifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", - "description" : "Authorization token to authenticate wallet requests" - } - } - }, - "CredAttrSpec" : { - "type" : "object", - "required" : [ "name", "value" ], - "properties" : { - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type: omit for (null) default", - "x-nullable" : true - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "value" : { - "type" : "string", - "example" : "martini", - "description" : "Attribute value: base64-encode if MIME type is present" - } - } - }, - "CredDefValue" : { - "type" : "object", - "properties" : { - "primary" : { - "$ref" : "#/definitions/CredDefValue_primary" - }, - "revocation" : { - "$ref" : "#/definitions/CredDefValue_revocation" - } - } - }, - "CredDefValuePrimary" : { - "type" : "object", - "properties" : { - "n" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "r" : { - "$ref" : "#/definitions/Generated" - }, - "rctxt" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "s" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "z" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - }, - "CredDefValueRevocation" : { - "type" : "object", - "properties" : { - "g" : { - "type" : "string", - "example" : "1 1F14F&ECB578F 2 095E45DDF417D" - }, - "g_dash" : { - "type" : "string", - "example" : "1 1D64716fCDC00C 1 0C781960FA66E3D3 2 095E45DDF417D" - }, - "h" : { - "type" : "string", - "example" : "1 16675DAE54BFAE8 2 095E45DD417D" - }, - "h0" : { - "type" : "string", - "example" : "1 21E5EF9476EAF18 2 095E45DDF417D" - }, - "h1" : { - "type" : "string", - "example" : "1 236D1D99236090 2 095E45DDF417D" - }, - "h2" : { - "type" : "string", - "example" : "1 1C3AE8D1F1E277 2 095E45DDF417D" - }, - "h_cap" : { - "type" : "string", - "example" : "1 1B2A32CF3167 1 2490FEBF6EE55 1 0000000000000000" - }, - "htilde" : { - "type" : "string", - "example" : "1 1D8549E8C0F8 2 095E45DDF417D" - }, - "pk" : { - "type" : "string", - "example" : "1 142CD5E5A7DC 1 153885BD903312 2 095E45DDF417D" - }, - "u" : { - "type" : "string", - "example" : "1 0C430AAB2B4710 1 1CB3A0932EE7E 1 0000000000000000" - }, - "y" : { - "type" : "string", - "example" : "1 153558BD903312 2 095E45DDF417D 1 0000000000000000" - } - } - }, - "CredInfoList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyCredInfo" - } - } - } - }, - "CredRevRecordResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/IssuerCredRevRecord" - } - } - }, - "CredRevokedResult" : { - "type" : "object", - "properties" : { - "revoked" : { - "type" : "boolean", - "description" : "Whether credential is revoked on the ledger" - } - } - }, - "Credential" : { - "type" : "object", - "required" : [ "@context", "credentialSubject", "issuanceDate", "issuer", "type" ], - "properties" : { - "@context" : { - "type" : "array", - "example" : [ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1" ], - "description" : "The JSON-LD context of the credential", - "items" : { } - }, - "credentialSubject" : { - "example" : "" - }, - "expirationDate" : { - "type" : "string", - "example" : "2010-01-01T19:23:24Z", - "description" : "The expiration date", - "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$" - }, - "id" : { - "type" : "string", - "example" : "http://example.edu/credentials/1872", - "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" - }, - "issuanceDate" : { - "type" : "string", - "example" : "2010-01-01T19:23:24Z", - "description" : "The issuance date", - "pattern" : "^([0-9]{4})-([0-9]{2})-([0-9]{2})([Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(\\.[0-9]+)?)?(([Zz]|([+-])([0-9]{2}):([0-9]{2})))?$" - }, - "issuer" : { - "example" : "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", - "description" : "The JSON-LD Verifiable Credential Issuer. Either string of object with id field." - }, - "proof" : { - "$ref" : "#/definitions/Credential_proof" - }, - "type" : { - "type" : "array", - "example" : [ "VerifiableCredential", "AlumniCredential" ], - "description" : "The JSON-LD type of the credential", - "items" : { - "type" : "string" - } - } - } - }, - "CredentialDefinition" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "schemaId" : { - "type" : "string", - "example" : "20", - "description" : "Schema identifier within credential definition identifier" - }, - "tag" : { - "type" : "string", - "example" : "tag", - "description" : "Tag within credential definition identifier" - }, - "type" : { - "example" : "CL", - "description" : "Signature type: CL for Camenisch-Lysyanskaya" - }, - "value" : { - "$ref" : "#/definitions/CredentialDefinition_value" - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Node protocol version", - "pattern" : "^[0-9.]+$" - } - } - }, - "CredentialDefinitionGetResult" : { - "type" : "object", - "properties" : { - "credential_definition" : { - "$ref" : "#/definitions/CredentialDefinition" - } - } - }, - "CredentialDefinitionSendRequest" : { - "type" : "object", - "properties" : { - "revocation_registry_size" : { - "type" : "integer", - "format" : "int32", - "example" : 1000, - "description" : "Revocation registry size", - "minimum" : 4, - "maximum" : 32768 - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "support_revocation" : { - "type" : "boolean", - "description" : "Revocation supported flag" - }, - "tag" : { - "type" : "string", - "example" : "default", - "description" : "Credential definition identifier tag" - } - } - }, - "CredentialDefinitionSendResult" : { - "type" : "object", - "properties" : { - "credential_definition_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } - } - }, - "CredentialDefinitionsCreatedResult" : { - "type" : "object", - "properties" : { - "credential_definition_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifiers", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - } - } - } - }, - "CredentialOffer" : { - "type" : "object", - "required" : [ "offers~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "offers~attach" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "CredentialPreview" : { - "type" : "object", - "required" : [ "attributes" ], - "properties" : { - "@type" : { - "type" : "string", - "example" : "issue-credential/1.0/credential-preview", - "description" : "Message type identifier" - }, - "attributes" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/CredAttrSpec" - } - } - } - }, - "CredentialProposal" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "pattern" : "^[0-9.]+$" - } - } - }, - "CredentialStatusOptions" : { - "type" : "object", - "required" : [ "type" ], - "properties" : { - "type" : { - "type" : "string", - "example" : "CredentialStatusList2017", - "description" : "Credential status method type to use for the credential. Should match status method registered in the Verifiable Credential Extension Registry" - } - } - }, - "DID" : { - "type" : "object", - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of interest", - "pattern" : "^did:key:z[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$|^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "key_type" : { - "type" : "string", - "example" : "ed25519", - "description" : "Key type associated with the DID", - "enum" : [ "ed25519", "bls12381g2" ] - }, - "method" : { - "type" : "string", - "example" : "sov", - "description" : "Did method associated with the DID", - "enum" : [ "sov", "key" ] - }, - "posture" : { - "type" : "string", - "example" : "wallet_only", - "description" : "Whether DID is current public DID, posted to ledger but not current public DID, or local to the wallet", - "enum" : [ "public", "posted", "wallet_only" ] - }, - "verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Public verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - } - }, - "DIDCreate" : { - "type" : "object", - "properties" : { - "method" : { - "type" : "string", - "example" : "sov", - "enum" : [ "key", "sov" ] - }, - "options" : { - "$ref" : "#/definitions/DIDCreate_options" - } - } - }, - "DIDCreateOptions" : { - "type" : "object", - "required" : [ "key_type" ], - "properties" : { - "key_type" : { - "type" : "string", - "example" : "ed25519", - "enum" : [ "ed25519", "bls12381g2" ] - } - } - }, - "DIDEndpoint" : { - "type" : "object", - "required" : [ "did" ], - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of interest", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Endpoint to set (omit to delete)", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - } - }, - "DIDEndpointWithType" : { - "type" : "object", - "required" : [ "did" ], - "properties" : { - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of interest", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Endpoint to set (omit to delete)", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "endpoint_type" : { - "type" : "string", - "example" : "Endpoint", - "description" : "Endpoint type to set (default 'Endpoint'); affects only public or posted DIDs", - "enum" : [ "Endpoint", "Profile", "LinkedDomains" ] - } - } - }, - "DIDList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "DID list", - "items" : { - "$ref" : "#/definitions/DID" - } - } - } - }, - "DIDResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/DID" - } - } - }, - "DIDXRequest" : { - "type" : "object", - "required" : [ "label" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID of exchange", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "did_doc~attach" : { - "$ref" : "#/definitions/DIDXRequest_did_docattach" - }, - "label" : { - "type" : "string", - "example" : "Request to connect with Bob", - "description" : "Label for DID exchange request" - } - } - }, - "DIFField" : { - "type" : "object", - "properties" : { - "filter" : { - "$ref" : "#/definitions/Filter" - }, - "id" : { - "type" : "string", - "description" : "ID" - }, - "path" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Path" - } - }, - "predicate" : { - "type" : "string", - "description" : "Preference", - "enum" : [ "required", "preferred" ] - }, - "purpose" : { - "type" : "string", - "description" : "Purpose" - } - } - }, - "DIFHolder" : { - "type" : "object", - "properties" : { - "directive" : { - "type" : "string", - "description" : "Preference", - "enum" : [ "required", "preferred" ] - }, - "field_id" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "FieldID", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - } - } - } - }, - "DIFOptions" : { - "type" : "object", - "properties" : { - "challenge" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Challenge protect against replay attack", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "domain" : { - "type" : "string", - "example" : "4jt78h47fh47", - "description" : "Domain protect against replay attack" - } - } - }, - "DIFPresSpec" : { - "type" : "object", - "properties" : { - "issuer_id" : { - "type" : "string", - "description" : "Issuer identifier to sign the presentation, if different from current public DID" - }, - "presentation_definition" : { - "$ref" : "#/definitions/PresentationDefinition" - }, - "record_ids" : { - "type" : "object", - "example" : { - "" : [ "", "" ], - "" : [ "" ] - }, - "description" : "Mapping of input_descriptor id to list of stored W3C credential record_id", - "properties" : { } - }, - "reveal_doc" : { - "type" : "object", - "example" : { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/security/bbs/v1" - ], - "type": ["VerifiableCredential", "LabReport"], - "@explicit": true, - "@requireAll": true, - "issuanceDate": {}, - "issuer": {}, - "credentialSubject": { - "Observation": [ - {"effectiveDateTime": {}, "@explicit": true, "@requireAll": true} - ], - "@explicit": true, - "@requireAll": true - } - }, - "description" : "reveal doc [JSON-LD frame] dict used to derive the credential when selective disclosure is required", - "properties" : { } - } - } - }, - "DIFProofProposal" : { - "type" : "object", - "properties" : { - "input_descriptors" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/InputDescriptors" - } - } - } - }, - "DIFProofRequest" : { - "type" : "object", - "required" : [ "presentation_definition" ], - "properties" : { - "options" : { - "$ref" : "#/definitions/DIFOptions" - }, - "presentation_definition" : { - "$ref" : "#/definitions/PresentationDefinition" - } - } - }, - "Date" : { - "type" : "object", - "required" : [ "expires_time" ], - "properties" : { - "expires_time" : { - "type" : "string", - "format" : "date-time", - "example" : "2021-03-29T05:22:19Z", - "description" : "Expiry Date" - } - } - }, - "Doc" : { - "type" : "object", - "required" : [ "credential", "options" ], - "properties" : { - "credential" : { - "type" : "object", - "description" : "Credential to sign", - "properties" : { } - }, - "options" : { - "$ref" : "#/definitions/Doc_options" - } - } - }, - "EndorserInfo" : { - "type" : "object", - "required" : [ "endorser_did" ], - "properties" : { - "endorser_did" : { - "type" : "string", - "description" : "Endorser DID" - }, - "endorser_name" : { - "type" : "string", - "description" : "Endorser Name" - } - } - }, - "EndpointsResult" : { - "type" : "object", - "properties" : { - "my_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "My endpoint", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - }, - "their_endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Their endpoint", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - } - }, - "Filter" : { - "type" : "object", - "properties" : { - "const" : { - "description" : "Const" - }, - "enum" : { - "type" : "array", - "items" : { - "description" : "Enum" - } - }, - "exclusiveMaximum" : { - "description" : "ExclusiveMaximum" - }, - "exclusiveMinimum" : { - "description" : "ExclusiveMinimum" - }, - "format" : { - "type" : "string", - "description" : "Format" - }, - "maxLength" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Max Length" - }, - "maximum" : { - "description" : "Maximum" - }, - "minLength" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Min Length" - }, - "minimum" : { - "description" : "Minimum" - }, - "not" : { - "type" : "boolean", - "example" : false, - "description" : "Not" - }, - "pattern" : { - "type" : "string", - "description" : "Pattern" - }, - "type" : { - "type" : "string", - "description" : "Type" - } - } - }, - "Generated" : { - "type" : "object", - "properties" : { - "master_secret" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "number" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "remainder" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - }, - "GetDIDEndpointResponse" : { - "type" : "object", - "properties" : { - "endpoint" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Full verification key", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$", - "x-nullable" : true - } - } - }, - "GetDIDVerkeyResponse" : { - "type" : "object", - "properties" : { - "verkey" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Full verification key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$", - "x-nullable" : true - } - } - }, - "GetNymRoleResponse" : { - "type" : "object", - "properties" : { - "role" : { - "type" : "string", - "example" : "ENDORSER", - "description" : "Ledger role", - "enum" : [ "STEWARD", "TRUSTEE", "ENDORSER", "NETWORK_MONITOR", "USER", "ROLE_REMOVE" ] - } - } - }, - "HolderModuleResponse" : { - "type" : "object" - }, - "IndyAttrValue" : { - "type" : "object", - "required" : [ "encoded", "raw" ], - "properties" : { - "encoded" : { - "type" : "string", - "example" : "0", - "description" : "Attribute encoded value", - "pattern" : "^[0-9]*$" - }, - "raw" : { - "type" : "string", - "description" : "Attribute raw value" - } - } - }, - "IndyCredAbstract" : { - "type" : "object", - "required" : [ "cred_def_id", "key_correctness_proof", "nonce", "schema_id" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "key_correctness_proof" : { - "$ref" : "#/definitions/IndyCredAbstract_key_correctness_proof" - }, - "nonce" : { - "type" : "string", - "example" : "0", - "description" : "Nonce in credential abstract", - "pattern" : "^[0-9]*$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - }, - "IndyCredInfo" : { - "type" : "object", - "properties" : { - "attrs" : { - "type" : "object", - "description" : "Attribute names and value", - "additionalProperties" : { - "type" : "string", - "example" : "alice" - } - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$", - "x-nullable" : true - }, - "referent" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet referent" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", - "x-nullable" : true - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - }, - "IndyCredPrecis" : { - "type" : "object", - "properties" : { - "cred_info" : { - "$ref" : "#/definitions/IndyCredPrecis_cred_info" - }, - "interval" : { - "$ref" : "#/definitions/IndyCredPrecis_interval" - }, - "presentation_referents" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "1_age_uuid", - "description" : "presentation referent" - } - } - } - }, - "IndyCredRequest" : { - "type" : "object", - "required" : [ "blinded_ms", "blinded_ms_correctness_proof", "cred_def_id", "nonce" ], - "properties" : { - "blinded_ms" : { - "type" : "object", - "description" : "Blinded master secret", - "properties" : { } - }, - "blinded_ms_correctness_proof" : { - "type" : "object", - "description" : "Blinded master secret correctness proof", - "properties" : { } - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "nonce" : { - "type" : "string", - "example" : "0", - "description" : "Nonce in credential request", - "pattern" : "^[0-9]*$" - }, - "prover_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Prover DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - } - } - }, - "IndyCredential" : { - "type" : "object", - "required" : [ "cred_def_id", "schema_id", "signature", "signature_correctness_proof", "values" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "rev_reg" : { - "type" : "object", - "description" : "Revocation registry state", - "properties" : { }, - "x-nullable" : true - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", - "x-nullable" : true - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "signature" : { - "type" : "object", - "description" : "Credential signature", - "properties" : { } - }, - "signature_correctness_proof" : { - "type" : "object", - "description" : "Credential signature correctness proof", - "properties" : { } - }, - "values" : { - "type" : "object", - "description" : "Credential attributes", - "additionalProperties" : { - "type" : "object", - "description" : "Attribute value", - "allOf" : [ { - "$ref" : "#/definitions/IndyAttrValue" - } ] - } - }, - "witness" : { - "type" : "object", - "description" : "Witness for revocation proof", - "properties" : { }, - "x-nullable" : true - } - } - }, - "IndyEQProof" : { - "type" : "object", - "properties" : { - "a_prime" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "e" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "m" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - }, - "m2" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "revealed_attrs" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - }, - "v" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - }, - "IndyGEProof" : { - "type" : "object", - "properties" : { - "alpha" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "mj" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - }, - "predicate" : { - "$ref" : "#/definitions/IndyGEProofPred" - }, - "r" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - }, - "t" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - }, - "u" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "0", - "pattern" : "^[0-9]*$" - } - } - } - }, - "IndyGEProofPred" : { - "type" : "object", - "properties" : { - "attr_name" : { - "type" : "string", - "description" : "Attribute name, indy-canonicalized" - }, - "p_type" : { - "type" : "string", - "description" : "Predicate type", - "enum" : [ "LT", "LE", "GE", "GT" ] - }, - "value" : { - "type" : "integer", - "format" : "int32", - "description" : "Predicate threshold value" - } - } - }, - "IndyKeyCorrectnessProof" : { - "type" : "object", - "required" : [ "c", "xr_cap", "xz_cap" ], - "properties" : { - "c" : { - "type" : "string", - "example" : "0", - "description" : "c in key correctness proof", - "pattern" : "^[0-9]*$" - }, - "xr_cap" : { - "type" : "array", - "description" : "xr_cap in key correctness proof", - "items" : { - "type" : "array", - "description" : "xr_cap components in key correctness proof", - "items" : { - "type" : "string", - "description" : "xr_cap component values in key correctness proof" - } - } - }, - "xz_cap" : { - "type" : "string", - "example" : "0", - "description" : "xz_cap in key correctness proof", - "pattern" : "^[0-9]*$" - } - } - }, - "IndyNonRevocProof" : { - "type" : "object", - "properties" : { - "c_list" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - }, - "x_list" : { - "type" : "object", - "additionalProperties" : { - "type" : "string" - } - } - } - }, - "IndyNonRevocationInterval" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyPresAttrSpec" : { - "type" : "object", - "required" : [ "name" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type (default null)" - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "referent" : { - "type" : "string", - "example" : "0", - "description" : "Credential referent" - }, - "value" : { - "type" : "string", - "example" : "martini", - "description" : "Attribute value" - } - } - }, - "IndyPresPredSpec" : { - "type" : "object", - "required" : [ "name", "predicate", "threshold" ], - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "name" : { - "type" : "string", - "example" : "high_score", - "description" : "Attribute name" - }, - "predicate" : { - "type" : "string", - "example" : ">=", - "description" : "Predicate type ('<', '<=', '>=', or '>')", - "enum" : [ "<", "<=", ">=", ">" ] - }, - "threshold" : { - "type" : "integer", - "format" : "int32", - "description" : "Threshold value" - } - } - }, - "IndyPresPreview" : { - "type" : "object", - "required" : [ "attributes", "predicates" ], - "properties" : { - "@type" : { - "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview", - "description" : "Message type identifier" - }, - "attributes" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyPresAttrSpec" - } - }, - "predicates" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/IndyPresPredSpec" - } - } - } - }, - "IndyPresSpec" : { - "type" : "object", - "required" : [ "requested_attributes", "requested_predicates", "self_attested_attributes" ], - "properties" : { - "requested_attributes" : { - "type" : "object", - "description" : "Nested object mapping proof request attribute referents to requested-attribute specifiers", - "additionalProperties" : { - "$ref" : "#/definitions/IndyRequestedCredsRequestedAttr" - } - }, - "requested_predicates" : { - "type" : "object", - "description" : "Nested object mapping proof request predicate referents to requested-predicate specifiers", - "additionalProperties" : { - "$ref" : "#/definitions/IndyRequestedCredsRequestedPred" - } - }, - "self_attested_attributes" : { - "type" : "object", - "description" : "Self-attested attributes to build into proof", - "additionalProperties" : { - "type" : "string", - "example" : "self_attested_value", - "description" : "Self-attested attribute values to use in requested-credentials structure for proof construction" - } - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "IndyPrimaryProof" : { - "type" : "object", - "properties" : { - "eq_proof" : { - "$ref" : "#/definitions/IndyPrimaryProof_eq_proof" - }, - "ge_proofs" : { - "type" : "array", - "description" : "Indy GE proofs", - "items" : { - "$ref" : "#/definitions/IndyGEProof" - }, - "x-nullable" : true - } - } - }, - "IndyProof" : { - "type" : "object", - "properties" : { - "identifiers" : { - "type" : "array", - "description" : "Indy proof.identifiers content", - "items" : { - "$ref" : "#/definitions/IndyProofIdentifier" - } - }, - "proof" : { - "$ref" : "#/definitions/IndyProof_proof" - }, - "requested_proof" : { - "$ref" : "#/definitions/IndyProof_requested_proof" - } - } - }, - "IndyProofIdentifier" : { - "type" : "object", - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)", - "x-nullable" : true - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "timestamp" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Timestamp epoch", - "minimum" : 0, - "maximum" : 18446744073709551615, - "x-nullable" : true - } - } - }, - "IndyProofProof" : { - "type" : "object", - "properties" : { - "aggregated_proof" : { - "$ref" : "#/definitions/IndyProofProof_aggregated_proof" - }, - "proofs" : { - "type" : "array", - "description" : "Indy proof proofs", - "items" : { - "$ref" : "#/definitions/IndyProofProofProofsProof" - } - } - } - }, - "IndyProofProofAggregatedProof" : { - "type" : "object", - "properties" : { - "c_hash" : { - "type" : "string", - "description" : "c_hash value" - }, - "c_list" : { - "type" : "array", - "description" : "c_list value", - "items" : { - "type" : "array", - "items" : { - "type" : "integer", - "format" : "int32" - } - } - } - } - }, - "IndyProofProofProofsProof" : { - "type" : "object", - "properties" : { - "non_revoc_proof" : { - "$ref" : "#/definitions/IndyProofProofProofsProof_non_revoc_proof" - }, - "primary_proof" : { - "$ref" : "#/definitions/IndyProofProofProofsProof_primary_proof" - } - } - }, - "IndyProofReqAttrSpec" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string", - "example" : "favouriteDrink", - "description" : "Attribute name" - }, - "names" : { - "type" : "array", - "description" : "Attribute name group", - "items" : { - "type" : "string", - "example" : "age" - } - }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" - }, - "restrictions" : { - "type" : "array", - "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", - "items" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag" - } - } - } - } - }, - "IndyProofReqAttrSpecNonRevoked" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyProofReqPredSpec" : { - "type" : "object", - "required" : [ "name", "p_type", "p_value" ], - "properties" : { - "name" : { - "type" : "string", - "example" : "index", - "description" : "Attribute name" - }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" - }, - "p_type" : { - "type" : "string", - "example" : ">=", - "description" : "Predicate type ('<', '<=', '>=', or '>')", - "enum" : [ "<", "<=", ">=", ">" ] - }, - "p_value" : { - "type" : "integer", - "format" : "int32", - "description" : "Threshold value" - }, - "restrictions" : { - "type" : "array", - "description" : "If present, credential must satisfy one of given restrictions: specify schema_id, schema_issuer_did, schema_name, schema_version, issuer_did, cred_def_id, and/or attr::::value where represents a credential attribute name", - "items" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag" - } - } - } - } - }, - "IndyProofReqPredSpecNonRevoked" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyProofRequest" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string", - "example" : "Proof request", - "description" : "Proof request name" - }, - "non_revoked" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec_non_revoked" - }, - "nonce" : { - "type" : "string", - "example" : "1", - "description" : "Nonce", - "pattern" : "^[1-9][0-9]*$" - }, - "requested_attributes" : { - "type" : "object", - "description" : "Requested attribute specifications of proof request", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofReqAttrSpec" - } - }, - "requested_predicates" : { - "type" : "object", - "description" : "Requested predicate specifications of proof request", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofReqPredSpec" - } - }, - "version" : { - "type" : "string", - "example" : "1.0", - "description" : "Proof request version", - "pattern" : "^[0-9.]+$" - } - } - }, - "IndyProofRequestNonRevoked" : { - "type" : "object", - "properties" : { - "from" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Earliest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - }, - "to" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Latest time of interest in non-revocation interval", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyProofRequestedProof" : { - "type" : "object", - "properties" : { - "predicates" : { - "type" : "object", - "description" : "Proof requested proof predicates.", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofRequestedProofPredicate" - } - }, - "revealed_attr_groups" : { - "type" : "object", - "description" : "Proof requested proof revealed attribute groups", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofRequestedProofRevealedAttrGroup" - }, - "x-nullable" : true - }, - "revealed_attrs" : { - "type" : "object", - "description" : "Proof requested proof revealed attributes", - "additionalProperties" : { - "$ref" : "#/definitions/IndyProofRequestedProofRevealedAttr" - }, - "x-nullable" : true - }, - "self_attested_attrs" : { - "type" : "object", - "description" : "Proof requested proof self-attested attributes", - "properties" : { } - }, - "unrevealed_attrs" : { - "type" : "object", - "description" : "Unrevealed attributes", - "properties" : { } - } - } - }, - "IndyProofRequestedProofPredicate" : { - "type" : "object", - "properties" : { - "sub_proof_index" : { - "type" : "integer", - "format" : "int32", - "description" : "Sub-proof index" - } - } - }, - "IndyProofRequestedProofRevealedAttr" : { - "type" : "object", - "properties" : { - "encoded" : { - "type" : "string", - "example" : "0", - "description" : "Encoded value", - "pattern" : "^[0-9]*$" - }, - "raw" : { - "type" : "string", - "description" : "Raw value" - }, - "sub_proof_index" : { - "type" : "integer", - "format" : "int32", - "description" : "Sub-proof index" - } - } - }, - "IndyProofRequestedProofRevealedAttrGroup" : { - "type" : "object", - "properties" : { - "sub_proof_index" : { - "type" : "integer", - "format" : "int32", - "description" : "Sub-proof index" - }, - "values" : { - "type" : "object", - "description" : "Indy proof requested proof revealed attr groups group value", - "additionalProperties" : { - "$ref" : "#/definitions/RawEncoded" - } - } - } - }, - "IndyRequestedCredsRequestedAttr" : { - "type" : "object", - "required" : [ "cred_id" ], - "properties" : { - "cred_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet credential identifier (typically but not necessarily a UUID)" - }, - "revealed" : { - "type" : "boolean", - "description" : "Whether to reveal attribute in proof (default true)" - } - } - }, - "IndyRequestedCredsRequestedPred" : { - "type" : "object", - "required" : [ "cred_id" ], - "properties" : { - "cred_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet credential identifier (typically but not necessarily a UUID)" - }, - "timestamp" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "description" : "Epoch timestamp of interest for non-revocation proof", - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "IndyRevRegDef" : { - "type" : "object", - "properties" : { - "credDefId" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Indy revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "revocDefType" : { - "type" : "string", - "example" : "CL_ACCUM", - "description" : "Revocation registry type (specify CL_ACCUM)", - "enum" : [ "CL_ACCUM" ] - }, - "tag" : { - "type" : "string", - "description" : "Revocation registry tag" - }, - "value" : { - "$ref" : "#/definitions/IndyRevRegDef_value" - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Version of revocation registry definition", - "pattern" : "^[0-9.]+$" - } - } - }, - "IndyRevRegDefValue" : { - "type" : "object", - "properties" : { - "issuanceType" : { - "type" : "string", - "description" : "Issuance type", - "enum" : [ "ISSUANCE_ON_DEMAND", "ISSUANCE_BY_DEFAULT" ] - }, - "maxCredNum" : { - "type" : "integer", - "format" : "int32", - "example" : 10, - "description" : "Maximum number of credentials; registry size", - "minimum" : 1 - }, - "publicKeys" : { - "$ref" : "#/definitions/IndyRevRegDefValue_publicKeys" - }, - "tailsHash" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Tails hash value", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "tailsLocation" : { - "type" : "string", - "description" : "Tails file location" - } - } - }, - "IndyRevRegDefValuePublicKeys" : { - "type" : "object", - "properties" : { - "accumKey" : { - "$ref" : "#/definitions/IndyRevRegDefValuePublicKeysAccumKey" - } - } - }, - "IndyRevRegDefValuePublicKeysAccumKey" : { - "type" : "object", - "properties" : { - "z" : { - "type" : "string", - "example" : "1 120F522F81E6B7 1 09F7A59005C4939854", - "description" : "Value for z" - } - } - }, - "IndyRevRegEntry" : { - "type" : "object", - "properties" : { - "value" : { - "$ref" : "#/definitions/IndyRevRegEntry_value" - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Version of revocation registry entry", - "pattern" : "^[0-9.]+$" - } - } - }, - "IndyRevRegEntryValue" : { - "type" : "object", - "properties" : { - "accum" : { - "type" : "string", - "example" : "21 11792B036AED0AAA12A4 4 298B2571FFC63A737", - "description" : "Accumulator value" - }, - "prevAccum" : { - "type" : "string", - "example" : "21 137AC810975E4 6 76F0384B6F23", - "description" : "Previous accumulator value" - }, - "revoked" : { - "type" : "array", - "description" : "Revoked credential revocation identifiers", - "items" : { - "type" : "integer", - "format" : "int32" - } - } - } - }, - "InputDescriptors" : { - "type" : "object", - "properties" : { - "constraints" : { - "$ref" : "#/definitions/Constraints" - }, - "group" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Group" - } - }, - "id" : { - "type" : "string", - "description" : "ID" - }, - "metadata" : { - "type" : "object", - "description" : "Metadata dictionary", - "properties" : { } - }, - "name" : { - "type" : "string", - "description" : "Name" - }, - "purpose" : { - "type" : "string", - "description" : "Purpose" - } - } - }, - "IntroModuleResponse" : { - "type" : "object" - }, - "InvitationCreateRequest" : { - "type" : "object", - "properties" : { - "alias" : { - "type" : "string", - "example" : "Barry", - "description" : "Alias for connection" - }, - "attachments" : { - "type" : "array", - "description" : "Optional invitation attachments", - "items" : { - "$ref" : "#/definitions/AttachmentDef" - } - }, - "handshake_protocols" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", - "description" : "Handshake protocol to specify in invitation" - } - }, - "mediation_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Identifier for active mediation record to be used", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "metadata" : { - "type" : "object", - "description" : "Optional metadata to attach to the connection created with the invitation", - "properties" : { } - }, - "my_label" : { - "type" : "string", - "example" : "Invitation to Barry", - "description" : "Label for connection invitation" - }, - "use_public_did" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to use public DID in invitation" - } - } - }, - "InvitationMessage" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "handshake_protocols" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/didexchange/1.0", - "description" : "Handshake protocol" - } - }, - "label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label" - }, - "requests~attach" : { - "type" : "array", - "description" : "Optional request attachment", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "services" : { - "type" : "array", - "example" : [ { - "did" : "WgWxqztrNooG92RXvxSTWv", - "id" : "string", - "recipientKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], - "routingKeys" : [ "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH" ], - "serviceEndpoint" : "http://192.168.56.101:8020", - "type" : "string" - }, "did:sov:WgWxqztrNooG92RXvxSTWv" ], - "items" : { - "description" : "Either a DIDComm service object (as per RFC0067) or a DID string." - } - } - } - }, - "InvitationRecord" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "invi_msg_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Invitation message identifier" - }, - "invitation" : { - "$ref" : "#/definitions/InvitationRecord_invitation" - }, - "invitation_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Invitation record identifier" - }, - "invitation_url" : { - "type" : "string", - "example" : "https://example.com/endpoint?c_i=eyJAdHlwZSI6ICIuLi4iLCAiLi4uIjogIi4uLiJ9XX0=", - "description" : "Invitation message URL" - }, - "state" : { - "type" : "string", - "example" : "await_response", - "description" : "Out of band message exchange state" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "InvitationResult" : { - "type" : "object", - "properties" : { - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "invitation" : { - "$ref" : "#/definitions/ConnectionInvitation" - }, - "invitation_url" : { - "type" : "string", - "example" : "http://192.168.56.101:8020/invite?c_i=eyJAdHlwZSI6Li4ufQ==", - "description" : "Invitation URL" - } - } - }, - "IssueCredentialModuleResponse" : { - "type" : "object" - }, - "IssuerCredRevRecord" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange record identifier at credential issue" - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - }, - "record_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer credential revocation record identifier" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "state" : { - "type" : "string", - "example" : "issued", - "description" : "Issue credential revocation record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "IssuerRevRegRecord" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "error_msg" : { - "type" : "string", - "example" : "Revocation registry undefined", - "description" : "Error message" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "max_cred_num" : { - "type" : "integer", - "format" : "int32", - "example" : 1000, - "description" : "Maximum number of credentials for revocation registry" - }, - "pending_pub" : { - "type" : "array", - "description" : "Credential revocation identifier for credential revoked and pending publication to ledger", - "items" : { - "type" : "string", - "example" : "23" - } - }, - "record_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer revocation registry record identifier" - }, - "revoc_def_type" : { - "type" : "string", - "example" : "CL_ACCUM", - "description" : "Revocation registry type (specify CL_ACCUM)", - "enum" : [ "CL_ACCUM" ] - }, - "revoc_reg_def" : { - "$ref" : "#/definitions/IssuerRevRegRecord_revoc_reg_def" - }, - "revoc_reg_entry" : { - "$ref" : "#/definitions/IssuerRevRegRecord_revoc_reg_entry" - }, - "revoc_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Issue revocation registry record state" - }, - "tag" : { - "type" : "string", - "description" : "Tag within issuer revocation registry identifier" - }, - "tails_hash" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Tails hash", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - }, - "tails_local_path" : { - "type" : "string", - "description" : "Local path to tails file" - }, - "tails_public_uri" : { - "type" : "string", - "description" : "Public URI for tails file" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "Keylist" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of keylist records", - "items" : { - "$ref" : "#/definitions/RouteRecord" - } - } - } - }, - "KeylistQuery" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "filter" : { - "type" : "object", - "example" : { - "filter" : { } - }, - "description" : "Query dictionary object", - "properties" : { } - }, - "paginate" : { - "$ref" : "#/definitions/KeylistQuery_paginate" - } - } - }, - "KeylistQueryFilterRequest" : { - "type" : "object", - "properties" : { - "filter" : { - "type" : "object", - "description" : "Filter for keylist query", - "properties" : { } - } - } - }, - "KeylistQueryPaginate" : { - "type" : "object", - "properties" : { - "limit" : { - "type" : "integer", - "format" : "int32", - "example" : 30, - "description" : "Limit for keylist query" - }, - "offset" : { - "type" : "integer", - "format" : "int32", - "example" : 0, - "description" : "Offset value for query" - } - } - }, - "KeylistUpdate" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "updates" : { - "type" : "array", - "description" : "List of update rules", - "items" : { - "$ref" : "#/definitions/KeylistUpdateRule" - } - } - } - }, - "KeylistUpdateRequest" : { - "type" : "object", - "properties" : { - "updates" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/KeylistUpdateRule" - } - } - } - }, - "KeylistUpdateRule" : { - "type" : "object", - "required" : [ "action", "recipient_key" ], - "properties" : { - "action" : { - "type" : "string", - "example" : "add", - "description" : "Action for specific key", - "enum" : [ "add", "remove" ] - }, - "recipient_key" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Key to remove or add", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - } - }, - "LDProofVCDetail" : { - "type" : "object", - "required" : [ "credential", "options" ], - "properties" : { - "credential" : { - "$ref" : "#/definitions/LDProofVCDetail_credential" - }, - "options" : { - "$ref" : "#/definitions/LDProofVCDetail_options" - } - } - }, - "LDProofVCDetailOptions" : { - "type" : "object", - "required" : [ "proofType" ], - "properties" : { - "challenge" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "A challenge to include in the proof. SHOULD be provided by the requesting party of the credential (=holder)" - }, - "created" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "The date and time of the proof (with a maximum accuracy in seconds). Defaults to current system time", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "credentialStatus" : { - "$ref" : "#/definitions/LDProofVCDetailOptions_credentialStatus" - }, - "domain" : { - "type" : "string", - "example" : "example.com", - "description" : "The intended domain of validity for the proof" - }, - "proofPurpose" : { - "type" : "string", - "example" : "assertionMethod", - "description" : "The proof purpose used for the proof. Should match proof purposes registered in the Linked Data Proofs Specification" - }, - "proofType" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "The proof type used for the proof. Should match suites registered in the Linked Data Cryptographic Suite Registry" - } - } - }, - "LedgerModulesResult" : { - "type" : "object" - }, - "LinkedDataProof" : { - "type" : "object", - "required" : [ "created", "proofPurpose", "type", "verificationMethod" ], - "properties" : { - "challenge" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Associates a challenge with a proof, for use with a proofPurpose such as authentication" - }, - "created" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "The string value of an ISO8601 combined date and time string generated by the Signature Algorithm", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "domain" : { - "type" : "string", - "example" : "example.com", - "description" : "A string value specifying the restricted domain of the signature.", - "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" - }, - "jws" : { - "type" : "string", - "example" : "eyJhbGciOiAiRWREUc2UsICJjcml0IjogWyJiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQ1Ch6YBKY7UBAjg6iBX5qBQ", - "description" : "Associates a Detached Json Web Signature with a proof" - }, - "nonce" : { - "type" : "string", - "example" : "CF69iO3nfvqRsRBNElE8b4wO39SyJHPM7Gg1nExltW5vSfQA1lvDCR/zXX1To0/4NLo==", - "description" : "The nonce" - }, - "proofPurpose" : { - "type" : "string", - "example" : "assertionMethod", - "description" : "Proof purpose" - }, - "proofValue" : { - "type" : "string", - "example" : "sy1AahqbzJQ63n9RtekmwzqZeVj494VppdAVJBnMYrTwft6cLJJGeTSSxCCJ6HKnRtwE7jjDh6sB2z2AAiZY9BBnCD8wUVgwqH3qchGRCuC2RugA4eQ9fUrR4Yuycac3caiaaay", - "description" : "The proof value of a proof" - }, - "type" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "Identifies the digital signature suite that was used to create the signature" - }, - "verificationMethod" : { - "type" : "string", - "example" : "did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL", - "description" : "Information used for proof verification", - "pattern" : "\\w+:(\\/?\\/?)[^\\s]+" - } - } - }, - "MediationCreateRequest" : { - "type" : "object", - "properties" : { - "mediator_terms" : { - "type" : "array", - "description" : "List of mediator rules for recipient", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the mediator requires the recipient to agree" - } - }, - "recipient_terms" : { - "type" : "array", - "description" : "List of recipient rules for mediation", - "items" : { - "type" : "string", - "description" : "Indicate terms to which the recipient requires the mediator to agree" - } - } - } - }, - "MediationDeny" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "mediator_terms" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Terms for mediator to agree" - } - }, - "recipient_terms" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Terms for recipient to agree" - } - } - } - }, - "MediationGrant" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "endpoint" : { - "type" : "string", - "example" : "http://192.168.56.102:8020/", - "description" : "endpoint on which messages destined for the recipient are received." - }, - "routing_keys" : { - "type" : "array", - "items" : { - "type" : "string", - "description" : "Keys to use for forward message packaging" - } - } - } - }, - "MediationList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of mediation records", - "items" : { - "$ref" : "#/definitions/MediationRecord" - } - } - } - }, - "MediationRecord" : { - "type" : "object", - "required" : [ "connection_id", "role" ], - "properties" : { - "connection_id" : { - "type" : "string" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "endpoint" : { - "type" : "string" - }, - "mediation_id" : { - "type" : "string" - }, - "mediator_terms" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "recipient_terms" : { - "type" : "array", - "items" : { - "type" : "string" - } - }, - "role" : { - "type" : "string" - }, - "routing_keys" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "Menu" : { - "type" : "object", - "required" : [ "options" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "description" : { - "type" : "string", - "example" : "This menu presents options", - "description" : "Introductory text for the menu" - }, - "errormsg" : { - "type" : "string", - "example" : "Error: item not found", - "description" : "An optional error message to display in menu header" - }, - "options" : { - "type" : "array", - "description" : "List of menu options", - "items" : { - "$ref" : "#/definitions/MenuOption" - } - }, - "title" : { - "type" : "string", - "example" : "My Menu", - "description" : "Menu title" - } - } - }, - "MenuForm" : { - "type" : "object", - "properties" : { - "description" : { - "type" : "string", - "example" : "Window preference settings", - "description" : "Additional descriptive text for menu form" - }, - "params" : { - "type" : "array", - "description" : "List of form parameters", - "items" : { - "$ref" : "#/definitions/MenuFormParam" - } - }, - "submit-label" : { - "type" : "string", - "example" : "Send", - "description" : "Alternative label for form submit button" - }, - "title" : { - "type" : "string", - "example" : "Preferences", - "description" : "Menu form title" - } - } - }, - "MenuFormParam" : { - "type" : "object", - "required" : [ "name", "title" ], - "properties" : { - "default" : { - "type" : "string", - "example" : "0", - "description" : "Default parameter value" - }, - "description" : { - "type" : "string", - "example" : "Delay in seconds before starting", - "description" : "Additional descriptive text for menu form parameter" - }, - "name" : { - "type" : "string", - "example" : "delay", - "description" : "Menu parameter name" - }, - "required" : { - "type" : "boolean", - "example" : false, - "description" : "Whether parameter is required" - }, - "title" : { - "type" : "string", - "example" : "Delay in seconds", - "description" : "Menu parameter title" - }, - "type" : { - "type" : "string", - "example" : "int", - "description" : "Menu form parameter input type" - } - } - }, - "MenuJson" : { - "type" : "object", - "required" : [ "options" ], - "properties" : { - "description" : { - "type" : "string", - "example" : "User preferences for window settings", - "description" : "Introductory text for the menu" - }, - "errormsg" : { - "type" : "string", - "example" : "Error: item not present", - "description" : "Optional error message to display in menu header" - }, - "options" : { - "type" : "array", - "description" : "List of menu options", - "items" : { - "$ref" : "#/definitions/MenuOption" - } - }, - "title" : { - "type" : "string", - "example" : "My Menu", - "description" : "Menu title" - } - } - }, - "MenuOption" : { - "type" : "object", - "required" : [ "name", "title" ], - "properties" : { - "description" : { - "type" : "string", - "example" : "Window display preferences", - "description" : "Additional descriptive text for menu option" - }, - "disabled" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to show option as disabled" - }, - "form" : { - "$ref" : "#/definitions/MenuForm" - }, - "name" : { - "type" : "string", - "example" : "window_prefs", - "description" : "Menu option name (unique identifier)" - }, - "title" : { - "type" : "string", - "example" : "Window Preferences", - "description" : "Menu option title" - } - } - }, - "MultitenantModuleResponse" : { - "type" : "object" - }, - "PerformRequest" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string", - "example" : "Query", - "description" : "Menu option name" - }, - "params" : { - "type" : "object", - "description" : "Input parameter values", - "additionalProperties" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6" - } - } - } - }, - "PingRequest" : { - "type" : "object", - "properties" : { - "comment" : { - "type" : "string", - "description" : "Comment for the ping message", - "x-nullable" : true - } - } - }, - "PingRequestResponse" : { - "type" : "object", - "properties" : { - "thread_id" : { - "type" : "string", - "description" : "Thread ID of the ping message" - } - } - }, - "PresentationDefinition" : { - "type" : "object", - "properties" : { - "format" : { - "$ref" : "#/definitions/ClaimFormat" - }, - "id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Unique Resource Identifier", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "input_descriptors" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/InputDescriptors" - } - }, - "name" : { - "type" : "string", - "description" : "Human-friendly name that describes what the presentation definition pertains to" - }, - "purpose" : { - "type" : "string", - "description" : "Describes the purpose for which the Presentation Definition's inputs are being requested" - }, - "submission_requirements" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/SubmissionRequirements" - } - } - } - }, - "PresentationProposal" : { - "type" : "object", - "required" : [ "presentation_proposal" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "presentation_proposal" : { - "$ref" : "#/definitions/IndyPresPreview" - } - } - }, - "PresentationRequest" : { - "type" : "object", - "required" : [ "request_presentations~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "request_presentations~attach" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "PublishRevocations" : { - "type" : "object", - "properties" : { - "rrid2crid" : { - "type" : "object", - "description" : "Credential revocation ids by revocation registry id", - "additionalProperties" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - } - } - } - } - }, - "QueryResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "object", - "description" : "Query results keyed by protocol", - "additionalProperties" : { - "type" : "object", - "description" : "Protocol descriptor", - "properties" : { } - } - } - } - }, - "RawEncoded" : { - "type" : "object", - "properties" : { - "encoded" : { - "type" : "string", - "example" : "0", - "description" : "Encoded value", - "pattern" : "^[0-9]*$" - }, - "raw" : { - "type" : "string", - "description" : "Raw value" - } - } - }, - "ReceiveInvitationRequest" : { - "type" : "object", - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "DID for connection invitation", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "imageUrl" : { - "type" : "string", - "format" : "url", - "example" : "http://192.168.56.101/img/logo.jpg", - "description" : "Optional image URL for connection invitation", - "x-nullable" : true - }, - "label" : { - "type" : "string", - "example" : "Bob", - "description" : "Optional label for connection invitation" - }, - "recipientKeys" : { - "type" : "array", - "description" : "List of recipient keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Recipient public key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "routingKeys" : { - "type" : "array", - "description" : "List of routing keys", - "items" : { - "type" : "string", - "example" : "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV", - "description" : "Routing key", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$" - } - }, - "serviceEndpoint" : { - "type" : "string", - "example" : "http://192.168.56.101:8020", - "description" : "Service endpoint at which to reach this agent" - } - } - }, - "RegisterLedgerNymResponse" : { - "type" : "object", - "properties" : { - "success" : { - "type" : "boolean", - "example" : true, - "description" : "Success of nym registration operation" - } - } - }, - "RemoveWalletRequest" : { - "type" : "object", - "properties" : { - "wallet_key" : { - "type" : "string", - "example" : "MySecretKey123", - "description" : "Master key used for key derivation. Only required for unmanaged wallets." - } - } - }, - "ResolutionResult" : { - "type" : "object", - "required" : [ "did_doc", "metadata" ], - "properties" : { - "did_doc" : { - "type" : "object", - "description" : "DID Document", - "properties" : { } - }, - "metadata" : { - "type" : "object", - "description" : "Resolution metadata", - "properties" : { } - } - } - }, - "RevRegCreateRequest" : { - "type" : "object", - "properties" : { - "credential_definition_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "max_cred_num" : { - "type" : "integer", - "format" : "int32", - "example" : 1000, - "description" : "Revocation registry size", - "minimum" : 4, - "maximum" : 32768 - } - } - }, - "RevRegIssuedResult" : { - "type" : "object", - "properties" : { - "result" : { - "type" : "integer", - "format" : "int32", - "example" : 0, - "description" : "Number of credentials issued against revocation registry", - "minimum" : 0 - } - } - }, - "RevRegResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/IssuerRevRegRecord" - } - } - }, - "RevRegUpdateTailsFileUri" : { - "type" : "object", - "required" : [ "tails_public_uri" ], - "properties" : { - "tails_public_uri" : { - "type" : "string", - "format" : "url", - "example" : "http://192.168.56.133:6543/revocation/registry/WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0/tails-file", - "description" : "Public URI to the tails file" - } - } - }, - "RevRegsCreated" : { - "type" : "object", - "properties" : { - "rev_reg_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifiers", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } - } - } - }, - "RevocationModuleResponse" : { - "type" : "object" - }, - "RevokeRequest" : { - "type" : "object", - "properties" : { - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange identifier", - "pattern" : "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier", - "pattern" : "^[1-9][0-9]*$" - }, - "publish" : { - "type" : "boolean", - "description" : "(True) publish revocation to ledger immediately, or (default, False) mark it pending" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - } - } - }, - "RouteRecord" : { - "type" : "object", - "required" : [ "recipient_key" ], - "properties" : { - "connection_id" : { - "type" : "string" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "recipient_key" : { - "type" : "string" - }, - "record_id" : { - "type" : "string" - }, - "role" : { - "type" : "string" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "wallet_id" : { - "type" : "string" - } - } - }, - "Schema" : { - "type" : "object", - "properties" : { - "attrNames" : { - "type" : "array", - "description" : "Schema attribute names", - "items" : { - "type" : "string", - "example" : "score", - "description" : "Attribute name" - } - }, - "id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "name" : { - "type" : "string", - "example" : "schema_name", - "description" : "Schema name" - }, - "seqNo" : { - "type" : "integer", - "format" : "int32", - "example" : 10, - "description" : "Schema sequence number", - "minimum" : 1 - }, - "ver" : { - "type" : "string", - "example" : "1.0", - "description" : "Node protocol version", - "pattern" : "^[0-9.]+$" - }, - "version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - } - } - }, - "SchemaGetResult" : { - "type" : "object", - "properties" : { - "schema" : { - "$ref" : "#/definitions/Schema" - } - } - }, - "SchemaInputDescriptor" : { - "type" : "object", - "properties" : { - "required" : { - "type" : "boolean", - "description" : "Required" - }, - "uri" : { - "type" : "string", - "description" : "URI" - } - } - }, - "SchemaSendRequest" : { - "type" : "object", - "required" : [ "attributes", "schema_name", "schema_version" ], - "properties" : { - "attributes" : { - "type" : "array", - "description" : "List of schema attributes", - "items" : { - "type" : "string", - "example" : "score", - "description" : "attribute name" - } - }, - "schema_name" : { - "type" : "string", - "example" : "prefs", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - } - } - }, - "SchemaSendResult" : { - "type" : "object", - "required" : [ "schema_id" ], - "properties" : { - "schema" : { - "$ref" : "#/definitions/SchemaSendResult_schema" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - }, - "SchemasCreatedResult" : { - "type" : "object", - "properties" : { - "schema_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifiers", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - } - } - } - }, - "SendMenu" : { - "type" : "object", - "required" : [ "menu" ], - "properties" : { - "menu" : { - "$ref" : "#/definitions/SendMenu_menu" - } - } - }, - "SendMessage" : { - "type" : "object", - "properties" : { - "content" : { - "type" : "string", - "example" : "Hello", - "description" : "Message content" - } - } - }, - "SignRequest" : { - "type" : "object", - "required" : [ "doc", "verkey" ], - "properties" : { - "doc" : { - "$ref" : "#/definitions/Doc" - }, - "verkey" : { - "type" : "string", - "description" : "Verkey to use for signing" - } - } - }, - "SignResponse" : { - "type" : "object", - "properties" : { - "error" : { - "type" : "string", - "description" : "Error text" - }, - "signed_doc" : { - "type" : "object", - "description" : "Signed document", - "properties" : { } - } - } - }, - "SignatureOptions" : { - "type" : "object", - "required" : [ "proofPurpose", "verificationMethod" ], - "properties" : { - "challenge" : { - "type" : "string" - }, - "domain" : { - "type" : "string" - }, - "proofPurpose" : { - "type" : "string" - }, - "type" : { - "type" : "string" - }, - "verificationMethod" : { - "type" : "string" - } - } - }, - "SignedDoc" : { - "type" : "object", - "required" : [ "proof" ], - "properties" : { - "proof" : { - "$ref" : "#/definitions/SignedDoc_proof" - } - } - }, - "SubmissionRequirements" : { - "type" : "object", - "properties" : { - "count" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Count Value" - }, - "from" : { - "type" : "string", - "description" : "From" - }, - "from_nested" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/SubmissionRequirements" - } - }, - "max" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Max Value" - }, - "min" : { - "type" : "integer", - "format" : "int32", - "example" : 1234, - "description" : "Min Value" - }, - "name" : { - "type" : "string", - "description" : "Name" - }, - "purpose" : { - "type" : "string", - "description" : "Purpose" - }, - "rule" : { - "type" : "string", - "description" : "Selection", - "enum" : [ "all", "pick" ] - } - } - }, - "TAAAccept" : { - "type" : "object", - "properties" : { - "mechanism" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "version" : { - "type" : "string" - } - } - }, - "TAAAcceptance" : { - "type" : "object", - "properties" : { - "mechanism" : { - "type" : "string" - }, - "time" : { - "type" : "integer", - "format" : "int32", - "example" : 1640995199, - "minimum" : 0, - "maximum" : 18446744073709551615 - } - } - }, - "TAAInfo" : { - "type" : "object", - "properties" : { - "aml_record" : { - "$ref" : "#/definitions/AMLRecord" - }, - "taa_accepted" : { - "$ref" : "#/definitions/TAAAcceptance" - }, - "taa_record" : { - "$ref" : "#/definitions/TAARecord" - }, - "taa_required" : { - "type" : "boolean" - } - } - }, - "TAARecord" : { - "type" : "object", - "properties" : { - "digest" : { - "type" : "string" - }, - "text" : { - "type" : "string" - }, - "version" : { - "type" : "string" - } - } - }, - "TAAResult" : { - "type" : "object", - "properties" : { - "result" : { - "$ref" : "#/definitions/TAAInfo" - } - } - }, - "TransactionJobs" : { - "type" : "object", - "properties" : { - "transaction_my_job" : { - "type" : "string", - "description" : "My transaction related job", - "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] - }, - "transaction_their_job" : { - "type" : "string", - "description" : "Their transaction related job", - "enum" : [ "TRANSACTION_AUTHOR", "TRANSACTION_ENDORSER", "reset" ] - } - } - }, - "TransactionList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of transaction records", - "items" : { - "$ref" : "#/definitions/TransactionRecord" - } - } - } - }, - "TransactionRecord" : { - "type" : "object", - "properties" : { - "_type" : { - "type" : "string", - "example" : "101", - "description" : "Transaction type" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "The connection identifier for thie particular transaction record" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "endorser_write_txn" : { - "type" : "boolean", - "example" : true, - "description" : "If True, Endorser will write the transaction after endorsing it" - }, - "formats" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "attach_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "format" : "dif/endorse-transaction/request@v1.0" - }, - "additionalProperties" : { - "type" : "string" - } - } - }, - "messages_attach" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "@id" : "143c458d-1b1c-40c7-ab85-4d16808ddf0a", - "data" : { - "json" : "{\"endorser\": \"V4SGRU86Z58d6TV7PBUe6f\",\"identifier\": \"LjgpST2rjsoxYegQDRm7EL\",\"operation\": {\"data\": {\"attr_names\": [\"first_name\", \"last_name\"],\"name\": \"test_schema\",\"version\": \"2.1\",},\"type\": \"101\",},\"protocolVersion\": 2,\"reqId\": 1597766666168851000,\"signatures\": {\"LjgpST2rjsox\": \"4ATKMn6Y9sTgwqaGTm7py2c2M8x1EVDTWKZArwyuPgjU\"},\"taaAcceptance\": {\"mechanism\": \"manual\",\"taaDigest\": \"f50fe2c2ab977006761d36bd6f23e4c6a7e0fc2feb9f62\",\"time\": 1597708800,}}" - }, - "mime-type" : "application/json" - }, - "properties" : { } - } - }, - "signature_request" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "author_goal_code" : "transaction.ledger.write", - "context" : "did:sov", - "method" : "add-signature", - "signature_type" : "", - "signer_goal_code" : "transaction.endorse" - }, - "properties" : { } - } - }, - "signature_response" : { - "type" : "array", - "items" : { - "type" : "object", - "example" : { - "context" : "did:sov", - "message_id" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "method" : "add-signature", - "signer_goal_code" : "transaction.refuse" - }, - "properties" : { } - } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread Identifier" - }, - "timing" : { - "type" : "object", - "example" : { - "expires_time" : "2020-12-13T17:29:06+0000" - }, - "properties" : { } - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "transaction_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Transaction identifier" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "TxnOrCredentialDefinitionSendResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/CredentialDefinitionSendResult" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrCredentialDefinitionSendResult_txn" - } - } - }, - "TxnOrPublishRevocationsResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/PublishRevocations" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrPublishRevocationsResult_txn" - } - } - }, - "TxnOrRevRegResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/RevRegResult" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrRevRegResult_txn" - } - } - }, - "TxnOrSchemaSendResult" : { - "type" : "object", - "properties" : { - "sent" : { - "$ref" : "#/definitions/TxnOrSchemaSendResult_sent" - }, - "txn" : { - "$ref" : "#/definitions/TxnOrSchemaSendResult_txn" - } - } - }, - "UpdateWalletRequest" : { - "type" : "object", - "properties" : { - "image_url" : { - "type" : "string", - "example" : "https://aries.ca/images/sample.png", - "description" : "Image url for this wallet. This image url is publicized (self-attested) to other agents as part of forming a connection." - }, - "label" : { - "type" : "string", - "example" : "Alice", - "description" : "Label for this wallet. This label is publicized (self-attested) to other agents as part of forming a connection." - }, - "wallet_dispatch_type" : { - "type" : "string", - "example" : "default", - "description" : "Webhook target dispatch type for this wallet. default - Dispatch only to webhooks associated with this wallet. base - Dispatch only to webhooks associated with the base wallet. both - Dispatch to both webhook targets.", - "enum" : [ "default", "both", "base" ] - }, - "wallet_webhook_urls" : { - "type" : "array", - "description" : "List of Webhook URLs associated with this subwallet", - "items" : { - "type" : "string", - "example" : "http://localhost:8022/webhooks", - "description" : "Optional webhook URL to receive webhook messages" - } - } - } - }, - "V10CredentialBoundOfferRequest" : { - "type" : "object", - "properties" : { - "counter_proposal" : { - "$ref" : "#/definitions/V10CredentialBoundOfferRequest_counter_proposal" - } - } - }, - "V10CredentialConnFreeOfferRequest" : { - "type" : "object", - "required" : [ "cred_def_id", "credential_preview" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_preview" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialCreate" : { - "type" : "object", - "required" : [ "credential_proposal" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialExchange" : { - "type" : "object", - "properties" : { - "auto_issue" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to issue to request in this credential exchange" - }, - "auto_offer" : { - "type" : "boolean", - "example" : false, - "description" : "Holder choice to accept offer in this credential exchange" - }, - "auto_remove" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to remove this credential exchange record when complete" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "credential" : { - "$ref" : "#/definitions/V10CredentialExchange_credential" - }, - "credential_definition_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_exchange_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange identifier" - }, - "credential_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier" - }, - "credential_offer" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_offer" - }, - "credential_offer_dict" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" - }, - "credential_proposal_dict" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" - }, - "credential_request" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_request" - }, - "credential_request_metadata" : { - "type" : "object", - "description" : "(Indy) credential request metadata", - "properties" : { } - }, - "error_msg" : { - "type" : "string", - "example" : "Credential definition identifier is not set in proposal", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Issue-credential exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "parent_thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Parent thread identifier" - }, - "raw_credential" : { - "$ref" : "#/definitions/V10CredentialExchange_raw_credential" - }, - "revoc_reg_id" : { - "type" : "string", - "description" : "Revocation registry identifier" - }, - "revocation_id" : { - "type" : "string", - "description" : "Credential identifier within revocation registry" - }, - "role" : { - "type" : "string", - "example" : "issuer", - "description" : "Issue-credential exchange role: holder or issuer", - "enum" : [ "holder", "issuer" ] - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "state" : { - "type" : "string", - "example" : "credential_acked", - "description" : "Issue-credential exchange state" - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V10CredentialExchangeListResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Aries#0036 v1.0 credential exchange records", - "items" : { - "$ref" : "#/definitions/V10CredentialExchange" - } - } - } - }, - "V10CredentialFreeOfferRequest" : { - "type" : "object", - "required" : [ "connection_id", "cred_def_id", "credential_preview" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_preview" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialIssueRequest" : { - "type" : "object", - "properties" : { - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - } - } - }, - "V10CredentialProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V10CredentialProposalRequestMand" : { - "type" : "object", - "required" : [ "connection_id", "credential_proposal" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialProposalRequestOpt" : { - "type" : "object", - "required" : [ "connection_id" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "credential_proposal" : { - "$ref" : "#/definitions/CredentialPreview" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V10CredentialStoreRequest" : { - "type" : "object", - "properties" : { - "credential_id" : { - "type" : "string" - } - } - }, - "V10PresentProofModuleResponse" : { - "type" : "object" - }, - "V10PresentationCreateRequestRequest" : { - "type" : "object", - "required" : [ "proof_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "proof_request" : { - "$ref" : "#/definitions/IndyProofRequest" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V10PresentationExchange" : { - "type" : "object", - "properties" : { - "auto_present" : { - "type" : "boolean", - "example" : false, - "description" : "Prover choice to auto-present proof as verifier requests" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "error_msg" : { - "type" : "string", - "example" : "Invalid structure", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Present-proof exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "presentation" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation" - }, - "presentation_exchange_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Presentation exchange identifier" - }, - "presentation_proposal_dict" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" - }, - "presentation_request" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_request" - }, - "presentation_request_dict" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" - }, - "role" : { - "type" : "string", - "example" : "prover", - "description" : "Present-proof exchange role: prover or verifier", - "enum" : [ "prover", "verifier" ] - }, - "state" : { - "type" : "string", - "example" : "verified", - "description" : "Present-proof exchange state" - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "verified" : { - "type" : "string", - "example" : "true", - "description" : "Whether presentation is verified: true or false", - "enum" : [ "true", "false" ] - } - } - }, - "V10PresentationExchangeList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Aries RFC 37 v1.0 presentation exchange records", - "items" : { - "$ref" : "#/definitions/V10PresentationExchange" - } - } - } - }, - "V10PresentationProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V10PresentationProposalRequest" : { - "type" : "object", - "required" : [ "connection_id", "presentation_proposal" ], - "properties" : { - "auto_present" : { - "type" : "boolean", - "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "presentation_proposal" : { - "$ref" : "#/definitions/IndyPresPreview" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V10PresentationSendRequestRequest" : { - "type" : "object", - "required" : [ "connection_id", "proof_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "proof_request" : { - "$ref" : "#/definitions/IndyProofRequest" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20CredAttrSpec" : { - "type" : "object", - "required" : [ "name", "value" ], - "properties" : { - "mime-type" : { - "type" : "string", - "example" : "image/jpeg", - "description" : "MIME type: omit for (null) default", - "x-nullable" : true - }, - "name" : { - "type" : "string", - "example" : "favourite_drink", - "description" : "Attribute name" - }, - "value" : { - "type" : "string", - "example" : "martini", - "description" : "Attribute value: base64-encode if MIME type is present" - } - } - }, - "V20CredBoundOfferRequest" : { - "type" : "object", - "properties" : { - "counter_preview" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_counter_preview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - } - } - }, - "V20CredExFree" : { - "type" : "object", - "required" : [ "connection_id", "filter" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20CredExRecord" : { - "type" : "object", - "properties" : { - "auto_issue" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to issue to request in this credential exchange" - }, - "auto_offer" : { - "type" : "boolean", - "example" : false, - "description" : "Holder choice to accept offer in this credential exchange" - }, - "auto_remove" : { - "type" : "boolean", - "example" : false, - "description" : "Issuer choice to remove this credential exchange record when complete" - }, - "by_format" : { - "$ref" : "#/definitions/V20CredExRecord_by_format" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential exchange identifier" - }, - "cred_issue" : { - "$ref" : "#/definitions/V20CredExRecord_cred_issue" - }, - "cred_offer" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_offer_dict" - }, - "cred_preview" : { - "$ref" : "#/definitions/V20CredExRecord_cred_preview" - }, - "cred_proposal" : { - "$ref" : "#/definitions/V10CredentialExchange_credential_proposal_dict" - }, - "cred_request" : { - "$ref" : "#/definitions/V20CredExRecord_cred_request" - }, - "error_msg" : { - "type" : "string", - "example" : "The front fell off", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Issue-credential exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "parent_thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Parent thread identifier" - }, - "role" : { - "type" : "string", - "example" : "issuer", - "description" : "Issue-credential exchange role: holder or issuer", - "enum" : [ "issuer", "holder" ] - }, - "state" : { - "type" : "string", - "example" : "done", - "description" : "Issue-credential exchange state", - "enum" : [ "proposal-sent", "proposal-received", "offer-sent", "offer-received", "request-sent", "request-received", "credential-issued", "credential-received", "done" ] - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V20CredExRecordByFormat" : { - "type" : "object", - "properties" : { - "cred_issue" : { - "type" : "object", - "properties" : { } - }, - "cred_offer" : { - "type" : "object", - "properties" : { } - }, - "cred_proposal" : { - "type" : "object", - "properties" : { } - }, - "cred_request" : { - "type" : "object", - "properties" : { } - } - } - }, - "V20CredExRecordDetail" : { - "type" : "object", - "properties" : { - "cred_ex_record" : { - "$ref" : "#/definitions/V20CredExRecordDetail_cred_ex_record" - }, - "indy" : { - "$ref" : "#/definitions/V20CredExRecordIndy" - }, - "ld_proof" : { - "$ref" : "#/definitions/V20CredExRecordLDProof" - } - } - }, - "V20CredExRecordIndy" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Corresponding v2.0 credential exchange record identifier" - }, - "cred_ex_indy_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Record identifier" - }, - "cred_id_stored" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier stored in wallet" - }, - "cred_request_metadata" : { - "type" : "object", - "description" : "Credential request metadata for indy holder", - "properties" : { } - }, - "cred_rev_id" : { - "type" : "string", - "example" : "12345", - "description" : "Credential revocation identifier within revocation registry", - "pattern" : "^[1-9][0-9]*$" - }, - "rev_reg_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:4:WgWxqztrNooG92RXvxSTWv:3:CL:20:tag:CL_ACCUM:0", - "description" : "Revocation registry identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):4:([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+))(:.+)?:CL_ACCUM:(.+$)" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V20CredExRecordLDProof" : { - "type" : "object", - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "cred_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Corresponding v2.0 credential exchange record identifier" - }, - "cred_ex_ld_proof_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Record identifier" - }, - "cred_id_stored" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Credential identifier stored in wallet" - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - } - } - }, - "V20CredExRecordListResult" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Credential exchange records and corresponding detail records", - "items" : { - "$ref" : "#/definitions/V20CredExRecordDetail" - } - } - } - }, - "V20CredFilter" : { - "type" : "object", - "properties" : { - "indy" : { - "$ref" : "#/definitions/V20CredFilter_indy" - }, - "ld_proof" : { - "$ref" : "#/definitions/V20CredFilter_ld_proof" - } - } - }, - "V20CredFilterIndy" : { - "type" : "object", - "properties" : { - "cred_def_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag", - "description" : "Credential definition identifier", - "pattern" : "^([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$" - }, - "issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Credential issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_id" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0", - "description" : "Schema identifier", - "pattern" : "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$" - }, - "schema_issuer_did" : { - "type" : "string", - "example" : "WgWxqztrNooG92RXvxSTWv", - "description" : "Schema issuer DID", - "pattern" : "^(did:sov:)?[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}$" - }, - "schema_name" : { - "type" : "string", - "example" : "preferences", - "description" : "Schema name" - }, - "schema_version" : { - "type" : "string", - "example" : "1.0", - "description" : "Schema version", - "pattern" : "^[0-9.]+$" - } - } - }, - "V20CredFilterLDProof" : { - "type" : "object", - "required" : [ "ld_proof" ], - "properties" : { - "ld_proof" : { - "$ref" : "#/definitions/V20CredFilter_ld_proof" - } - } - }, - "V20CredFormat" : { - "type" : "object", - "required" : [ "attach_id", "format" ], - "properties" : { - "attach_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Attachment identifier" - }, - "format" : { - "type" : "string", - "example" : "aries/ld-proof-vc-detail@v1.0", - "description" : "Attachment format specifier" - } - } - }, - "V20CredIssue" : { - "type" : "object", - "required" : [ "credentials~attach", "formats" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credentials~attach" : { - "type" : "array", - "description" : "Credential attachments", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "formats" : { - "type" : "array", - "description" : "Acceptable attachment formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - }, - "replacement_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer-unique identifier to coordinate credential replacement" - } - } - }, - "V20CredIssueProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V20CredIssueRequest" : { - "type" : "object", - "properties" : { - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - } - } - }, - "V20CredOffer" : { - "type" : "object", - "required" : [ "formats", "offers~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "formats" : { - "type" : "array", - "description" : "Acceptable credential formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - }, - "offers~attach" : { - "type" : "array", - "description" : "Offer attachments", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "replacement_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Issuer-unique identifier to coordinate credential replacement" - } - } - }, - "V20CredOfferConnFreeRequest" : { - "type" : "object", - "required" : [ "filter" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20CredOfferRequest" : { - "type" : "object", - "required" : [ "connection_id", "filter" ], - "properties" : { - "auto_issue" : { - "type" : "boolean", - "description" : "Whether to respond automatically to credential requests, creating and issuing requested credentials" - }, - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20CredPreview" : { - "type" : "object", - "required" : [ "attributes" ], - "properties" : { - "@type" : { - "type" : "string", - "example" : "issue-credential/2.0/credential-preview", - "description" : "Message type identifier" - }, - "attributes" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/V20CredAttrSpec" - } - } - } - }, - "V20CredProposal" : { - "type" : "object", - "required" : [ "filters~attach", "formats" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredProposal_credential_preview" - }, - "filters~attach" : { - "type" : "array", - "description" : "Credential filter per acceptable format on corresponding identifier", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "formats" : { - "type" : "array", - "description" : "Attachment formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - } - } - }, - "V20CredRequest" : { - "type" : "object", - "required" : [ "formats", "requests~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "formats" : { - "type" : "array", - "description" : "Acceptable attachment formats", - "items" : { - "$ref" : "#/definitions/V20CredFormat" - } - }, - "requests~attach" : { - "type" : "array", - "description" : "Request attachments", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "V20CredRequestFree" : { - "type" : "object", - "required" : [ "connection_id", "filter" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "holder_did" : { - "type" : "string", - "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", - "description" : "Holder DID to substitute for the credentialSubject.id", - "x-nullable" : true - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20CredRequestRequest" : { - "type" : "object", - "properties" : { - "holder_did" : { - "type" : "string", - "example" : "did:key:ahsdkjahsdkjhaskjdhakjshdkajhsdkjahs", - "description" : "Holder DID to substitute for the credentialSubject.id", - "x-nullable" : true - } - } - }, - "V20CredStoreRequest" : { - "type" : "object", - "properties" : { - "credential_id" : { - "type" : "string" - } - } - }, - "V20IssueCredSchemaCore" : { - "type" : "object", - "required" : [ "filter" ], - "properties" : { - "auto_remove" : { - "type" : "boolean", - "description" : "Whether to remove the credential exchange record on completion (overrides --preserve-exchange-records configuration setting)" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "credential_preview" : { - "$ref" : "#/definitions/V20CredPreview" - }, - "filter" : { - "$ref" : "#/definitions/V20CredBoundOfferRequest_filter" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20IssueCredentialModuleResponse" : { - "type" : "object" - }, - "V20Pres" : { - "type" : "object", - "required" : [ "formats", "presentations~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "formats" : { - "type" : "array", - "description" : "Acceptable attachment formats", - "items" : { - "$ref" : "#/definitions/V20PresFormat" - } - }, - "presentations~attach" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "V20PresCreateRequestRequest" : { - "type" : "object", - "required" : [ "presentation_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "presentation_request" : { - "$ref" : "#/definitions/V20PresRequestByFormat" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20PresExRecord" : { - "type" : "object", - "properties" : { - "auto_present" : { - "type" : "boolean", - "example" : false, - "description" : "Prover choice to auto-present proof as verifier requests" - }, - "by_format" : { - "$ref" : "#/definitions/V20PresExRecord_by_format" - }, - "connection_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "error_msg" : { - "type" : "string", - "example" : "Invalid structure", - "description" : "Error message" - }, - "initiator" : { - "type" : "string", - "example" : "self", - "description" : "Present-proof exchange initiator: self or external", - "enum" : [ "self", "external" ] - }, - "pres" : { - "$ref" : "#/definitions/V20PresExRecord_pres" - }, - "pres_ex_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Presentation exchange identifier" - }, - "pres_proposal" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_proposal_dict" - }, - "pres_request" : { - "$ref" : "#/definitions/V10PresentationExchange_presentation_request_dict" - }, - "role" : { - "type" : "string", - "example" : "prover", - "description" : "Present-proof exchange role: prover or verifier", - "enum" : [ "prover", "verifier" ] - }, - "state" : { - "type" : "string", - "description" : "Present-proof exchange state", - "enum" : [ "proposal-sent", "proposal-received", "request-sent", "request-received", "presentation-sent", "presentation-received", "done", "abandoned" ] - }, - "thread_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Thread identifier" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "verified" : { - "type" : "string", - "example" : "true", - "description" : "Whether presentation is verified: 'true' or 'false'", - "enum" : [ "true", "false" ] - } - } - }, - "V20PresExRecordByFormat" : { - "type" : "object", - "properties" : { - "pres" : { - "type" : "object", - "properties" : { } - }, - "pres_proposal" : { - "type" : "object", - "properties" : { } - }, - "pres_request" : { - "type" : "object", - "properties" : { } - } - } - }, - "V20PresExRecordList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "Presentation exchange records", - "items" : { - "$ref" : "#/definitions/V20PresExRecord" - } - } - } - }, - "V20PresFormat" : { - "type" : "object", - "required" : [ "attach_id", "format" ], - "properties" : { - "attach_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Attachment identifier" - }, - "format" : { - "type" : "string", - "example" : "dif/presentation-exchange/submission@v1.0", - "description" : "Attachment format specifier" - } - } - }, - "V20PresProblemReportRequest" : { - "type" : "object", - "required" : [ "description" ], - "properties" : { - "description" : { - "type" : "string" - } - } - }, - "V20PresProposal" : { - "type" : "object", - "required" : [ "formats", "proposals~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment" - }, - "formats" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/V20PresFormat" - } - }, - "proposals~attach" : { - "type" : "array", - "description" : "Attachment per acceptable format on corresponding identifier", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - } - } - }, - "V20PresProposalByFormat" : { - "type" : "object", - "properties" : { - "dif" : { - "$ref" : "#/definitions/V20PresProposalByFormat_dif" - }, - "indy" : { - "$ref" : "#/definitions/V20PresProposalByFormat_indy" - } - } - }, - "V20PresProposalRequest" : { - "type" : "object", - "required" : [ "connection_id", "presentation_proposal" ], - "properties" : { - "auto_present" : { - "type" : "boolean", - "description" : "Whether to respond automatically to presentation requests, building and presenting requested proof" - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "presentation_proposal" : { - "$ref" : "#/definitions/V20PresProposalByFormat" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20PresRequest" : { - "type" : "object", - "required" : [ "formats", "request_presentations~attach" ], - "properties" : { - "@id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Message identifier" - }, - "@type" : { - "type" : "string", - "example" : "https://didcomm.org/my-family/1.0/my-message-type", - "description" : "Message type", - "readOnly" : true - }, - "comment" : { - "type" : "string", - "description" : "Human-readable comment" - }, - "formats" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/V20PresFormat" - } - }, - "request_presentations~attach" : { - "type" : "array", - "description" : "Attachment per acceptable format on corresponding identifier", - "items" : { - "$ref" : "#/definitions/AttachDecorator" - } - }, - "will_confirm" : { - "type" : "boolean", - "description" : "Whether verifier will send confirmation ack" - } - } - }, - "V20PresRequestByFormat" : { - "type" : "object", - "properties" : { - "dif" : { - "$ref" : "#/definitions/V20PresRequestByFormat_dif" - }, - "indy" : { - "$ref" : "#/definitions/V20PresRequestByFormat_indy" - } - } - }, - "V20PresSendRequestRequest" : { - "type" : "object", - "required" : [ "connection_id", "presentation_request" ], - "properties" : { - "comment" : { - "type" : "string", - "x-nullable" : true - }, - "connection_id" : { - "type" : "string", - "format" : "uuid", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Connection identifier" - }, - "presentation_request" : { - "$ref" : "#/definitions/V20PresRequestByFormat" - }, - "trace" : { - "type" : "boolean", - "example" : false, - "description" : "Whether to trace event (default false)" - } - } - }, - "V20PresSpecByFormatRequest" : { - "type" : "object", - "properties" : { - "dif" : { - "$ref" : "#/definitions/V20PresSpecByFormatRequest_dif" - }, - "indy" : { - "$ref" : "#/definitions/V20PresSpecByFormatRequest_indy" - }, - "trace" : { - "type" : "boolean", - "description" : "Record trace information, based on agent configuration" - } - } - }, - "V20PresentProofModuleResponse" : { - "type" : "object" - }, - "VCRecord" : { - "type" : "object", - "properties" : { - "contexts" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Context", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - }, - "cred_tags" : { - "type" : "object", - "additionalProperties" : { - "type" : "string", - "description" : "Retrieval tag value" - } - }, - "cred_value" : { - "type" : "object", - "description" : "(JSON-serializable) credential value", - "properties" : { } - }, - "expanded_types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://w3id.org/citizenship#PermanentResidentCard", - "description" : "JSON-LD expanded type extracted from type and context" - } - }, - "given_id" : { - "type" : "string", - "example" : "http://example.edu/credentials/3732", - "description" : "Credential identifier" - }, - "issuer_id" : { - "type" : "string", - "example" : "https://example.edu/issuers/14", - "description" : "Issuer identifier" - }, - "proof_types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "Signature suite used for proof" - } - }, - "record_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Record identifier" - }, - "schema_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://example.org/examples/degree.json", - "description" : "Schema identifier" - } - }, - "subject_ids" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "did:example:ebfeb1f712ebc6f1c276e12ec21", - "description" : "Subject identifier" - } - } - } - }, - "VCRecordList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "items" : { - "$ref" : "#/definitions/VCRecord" - } - } - } - }, - "VerifyRequest" : { - "type" : "object", - "required" : [ "doc" ], - "properties" : { - "doc" : { - "$ref" : "#/definitions/VerifyRequest_doc" - }, - "verkey" : { - "type" : "string", - "description" : "Verkey to use for doc verification" - } - } - }, - "VerifyResponse" : { - "type" : "object", - "required" : [ "valid" ], - "properties" : { - "error" : { - "type" : "string", - "description" : "Error text" - }, - "valid" : { - "type" : "boolean" - } - } - }, - "W3CCredentialsListRequest" : { - "type" : "object", - "properties" : { - "contexts" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Credential context to match", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - }, - "given_id" : { - "type" : "string", - "description" : "Given credential id to match" - }, - "issuer_id" : { - "type" : "string", - "description" : "Credential issuer identifier to match" - }, - "max_results" : { - "type" : "integer", - "format" : "int32", - "description" : "Maximum number of results to return" - }, - "proof_types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "Ed25519Signature2018", - "description" : "Signature suite used for proof" - } - }, - "schema_ids" : { - "type" : "array", - "description" : "Schema identifiers, all of which to match", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Credential schema identifier", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - }, - "subject_ids" : { - "type" : "array", - "description" : "Subject identifiers, all of which to match", - "items" : { - "type" : "string", - "description" : "Subject identifier" - } - }, - "tag_query" : { - "type" : "object", - "description" : "Tag filter", - "additionalProperties" : { - "type" : "string", - "description" : "Tag value" - } - }, - "types" : { - "type" : "array", - "items" : { - "type" : "string", - "example" : "https://myhost:8021", - "description" : "Credential type to match", - "pattern" : "^[A-Za-z0-9\\.\\-\\+]+://([A-Za-z0-9][.A-Za-z0-9-_]+[A-Za-z0-9])+(:[1-9][0-9]*)?(/[^?&#]+)?$" - } - } - } - }, - "WalletList" : { - "type" : "object", - "properties" : { - "results" : { - "type" : "array", - "description" : "List of wallet records", - "items" : { - "$ref" : "#/definitions/WalletRecord" - } - } - } - }, - "WalletModuleResponse" : { - "type" : "object" - }, - "WalletRecord" : { - "type" : "object", - "required" : [ "key_management_mode", "wallet_id" ], - "properties" : { - "created_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of record creation", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "key_management_mode" : { - "type" : "string", - "description" : "Mode regarding management of wallet key", - "enum" : [ "managed", "unmanaged" ] - }, - "settings" : { - "type" : "object", - "description" : "Settings for this wallet.", - "properties" : { } - }, - "state" : { - "type" : "string", - "example" : "active", - "description" : "Current record state" - }, - "updated_at" : { - "type" : "string", - "example" : "2021-12-31 23:59:59Z", - "description" : "Time of last record update", - "pattern" : "^\\d{4}-\\d\\d-\\d\\d[T ]\\d\\d:\\d\\d(?:\\:(?:\\d\\d(?:\\.\\d{1,6})?))?(?:[+-]\\d\\d:?\\d\\d|Z|)$" - }, - "wallet_id" : { - "type" : "string", - "example" : "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "description" : "Wallet record ID" - } - } - }, - "ActionMenuFetchResult_result" : { - "type" : "object", - "description" : "Action menu" - }, - "AttachDecoratorData_jws" : { - "type" : "object", - "description" : "Detached Java Web Signature" - }, - "CredDefValue_primary" : { - "type" : "object", - "description" : "Primary value for credential definition" - }, - "CredDefValue_revocation" : { - "type" : "object", - "description" : "Revocation value for credential definition" - }, - "Credential_proof" : { - "type" : "object", - "description" : "The proof of the credential", - "example" : "{\"created\":\"2019-12-11T03:50:55\",\"jws\":\"eyJhbGciOiAiRWREU0EiLCAiYjY0IjogZmFsc2UsICJjcml0JiNjQiXX0..lKJU0Df_keblRKhZAS9Qq6zybm-HqUXNVZ8vgEPNTAjQKBhQDxvXNo7nvtUBb_Eq1Ch6YBKY5qBQ\",\"proofPurpose\":\"assertionMethod\",\"type\":\"Ed25519Signature2018\",\"verificationMethod\":\"did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL#z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL\"}" - }, - "CredentialDefinition_value" : { - "type" : "object", - "description" : "Credential definition primary and revocation values" - }, - "DIDCreate_options" : { - "type" : "object", - "description" : "To define a key type for a did:key" - }, - "DIDXRequest_did_docattach" : { - "type" : "object", - "description" : "As signed attachment, DID Doc associated with DID" - }, - "Doc_options" : { - "type" : "object", - "description" : "Signature options" - }, - "IndyCredAbstract_key_correctness_proof" : { - "type" : "object", - "description" : "Key correctness proof" - }, - "IndyCredPrecis_cred_info" : { - "type" : "object", - "description" : "Credential info" - }, - "IndyCredPrecis_interval" : { - "type" : "object", - "description" : "Non-revocation interval from presentation request" - }, - "IndyPrimaryProof_eq_proof" : { - "type" : "object", - "description" : "Indy equality proof", - "x-nullable" : true - }, - "IndyProof_proof" : { - "type" : "object", - "description" : "Indy proof.proof content" - }, - "IndyProof_requested_proof" : { - "type" : "object", - "description" : "Indy proof.requested_proof content" - }, - "IndyProofProof_aggregated_proof" : { - "type" : "object", - "description" : "Indy proof aggregated proof" - }, - "IndyProofProofProofsProof_non_revoc_proof" : { - "type" : "object", - "description" : "Indy non-revocation proof", - "x-nullable" : true - }, - "IndyProofProofProofsProof_primary_proof" : { - "type" : "object", - "description" : "Indy primary proof" - }, - "IndyProofReqAttrSpec_non_revoked" : { - "type" : "object", - "x-nullable" : true - }, - "IndyRevRegDef_value" : { - "type" : "object", - "description" : "Revocation registry definition value" - }, - "IndyRevRegDefValue_publicKeys" : { - "type" : "object", - "description" : "Public keys" - }, - "IndyRevRegEntry_value" : { - "type" : "object", - "description" : "Revocation registry entry value" - }, - "InvitationRecord_invitation" : { - "type" : "object", - "description" : "Out of band invitation message" - }, - "IssuerRevRegRecord_revoc_reg_def" : { - "type" : "object", - "description" : "Revocation registry definition" - }, - "IssuerRevRegRecord_revoc_reg_entry" : { - "type" : "object", - "description" : "Revocation registry entry" - }, - "KeylistQuery_paginate" : { - "type" : "object", - "description" : "Pagination info" - }, - "LDProofVCDetail_credential" : { - "type" : "object", - "description" : "Detail of the JSON-LD Credential to be issued", - "example" : "{\"@context\":[\"https://www.w3.org/2018/credentials/v1\",\"https://w3id.org/citizenship/v1\"],\"credentialSubject\":{\"familyName\":\"SMITH\",\"gender\":\"Male\",\"givenName\":\"JOHN\",\"type\":[\"PermanentResident\",\"Person\"]},\"description\":\"Government of Example Permanent Resident Card.\",\"identifier\":\"83627465\",\"issuanceDate\":\"2019-12-03T12:19:52Z\",\"issuer\":\"did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th\",\"name\":\"Permanent Resident Card\",\"type\":[\"VerifiableCredential\",\"PermanentResidentCard\"]}" - }, - "LDProofVCDetail_options" : { - "type" : "object", - "description" : "Options for specifying how the linked data proof is created.", - "example" : "{\"proofType\":\"Ed25519Signature2018\"}" - }, - "LDProofVCDetailOptions_credentialStatus" : { - "type" : "object", - "description" : "The credential status mechanism to use for the credential. Omitting the property indicates the issued credential will not include a credential status" - }, - "SchemaSendResult_schema" : { - "type" : "object", - "description" : "Schema definition" - }, - "SendMenu_menu" : { - "type" : "object", - "description" : "Menu to send to connection" - }, - "SignedDoc_proof" : { - "type" : "object", - "description" : "Linked data proof" - }, - "TxnOrCredentialDefinitionSendResult_txn" : { - "type" : "object", - "description" : "Credential definition transaction to endorse" - }, - "TxnOrPublishRevocationsResult_txn" : { - "type" : "object", - "description" : "Revocation registry revocations transaction to endorse" - }, - "TxnOrRevRegResult_txn" : { - "type" : "object", - "description" : "Revocation registry definition transaction to endorse" - }, - "TxnOrSchemaSendResult_sent" : { - "type" : "object", - "description" : "Content sent" - }, - "TxnOrSchemaSendResult_txn" : { - "type" : "object", - "description" : "Schema transaction to endorse" - }, - "V10CredentialBoundOfferRequest_counter_proposal" : { - "type" : "object", - "description" : "Optional counter-proposal" - }, - "V10CredentialExchange_credential" : { - "type" : "object", - "description" : "Credential as stored" - }, - "V10CredentialExchange_credential_offer" : { - "type" : "object", - "description" : "(Indy) credential offer" - }, - "V10CredentialExchange_credential_offer_dict" : { - "type" : "object", - "description" : "Credential offer message" - }, - "V10CredentialExchange_credential_proposal_dict" : { - "type" : "object", - "description" : "Credential proposal message" - }, - "V10CredentialExchange_credential_request" : { - "type" : "object", - "description" : "(Indy) credential request" - }, - "V10CredentialExchange_raw_credential" : { - "type" : "object", - "description" : "Credential as received, prior to storage in holder wallet" - }, - "V10PresentationExchange_presentation" : { - "type" : "object", - "description" : "(Indy) presentation (also known as proof)" - }, - "V10PresentationExchange_presentation_proposal_dict" : { - "type" : "object", - "description" : "Presentation proposal message" - }, - "V10PresentationExchange_presentation_request" : { - "type" : "object", - "description" : "(Indy) presentation request (also known as proof request)" - }, - "V10PresentationExchange_presentation_request_dict" : { - "type" : "object", - "description" : "Presentation request message" - }, - "V20CredBoundOfferRequest_counter_preview" : { - "type" : "object", - "description" : "Optional content for counter-proposal" - }, - "V20CredBoundOfferRequest_filter" : { - "type" : "object", - "description" : "Credential specification criteria by format" - }, - "V20CredExRecord_by_format" : { - "type" : "object", - "description" : "Attachment content by format for proposal, offer, request, and issue" - }, - "V20CredExRecord_cred_issue" : { - "type" : "object", - "description" : "Serialized credential issue message" - }, - "V20CredExRecord_cred_preview" : { - "type" : "object", - "description" : "Credential preview from credential proposal" - }, - "V20CredExRecord_cred_request" : { - "type" : "object", - "description" : "Serialized credential request message" - }, - "V20CredExRecordDetail_cred_ex_record" : { - "type" : "object", - "description" : "Credential exchange record" - }, - "V20CredFilter_indy" : { - "type" : "object", - "description" : "Credential filter for indy" - }, - "V20CredFilter_ld_proof" : { - "type" : "object", - "description" : "Credential filter for linked data proof" - }, - "V20CredProposal_credential_preview" : { - "type" : "object", - "description" : "Credential preview" - }, - "V20PresExRecord_by_format" : { - "type" : "object", - "description" : "Attachment content by format for proposal, request, and presentation" - }, - "V20PresExRecord_pres" : { - "type" : "object", - "description" : "Presentation message" - }, - "V20PresProposalByFormat_dif" : { - "type" : "object", - "description" : "Presentation proposal for DIF" - }, - "V20PresProposalByFormat_indy" : { - "type" : "object", - "description" : "Presentation proposal for indy" - }, - "V20PresRequestByFormat_dif" : { - "type" : "object", - "description" : "Presentation request for DIF" - }, - "V20PresRequestByFormat_indy" : { - "type" : "object", - "description" : "Presentation request for indy" - }, - "V20PresSpecByFormatRequest_dif" : { - "type" : "object", - "description" : "Optional Presentation specification for DIF, overrides the PresentionExchange record's PresRequest" - }, - "V20PresSpecByFormatRequest_indy" : { - "type" : "object", - "description" : "Presentation specification for indy" - }, - "VerifyRequest_doc" : { - "type" : "object", - "description" : "Signed document" - } - } -} \ No newline at end of file diff --git a/examples/godiddy-api.yml b/examples/godiddy-api.yml deleted file mode 100644 index 33ce581db2..0000000000 --- a/examples/godiddy-api.yml +++ /dev/null @@ -1,1387 +0,0 @@ -openapi: 3.0.3 -servers: - - url: /0.1.0 -info: - description: See https://docs.godiddy.com/ for additional documentation. - version: 0.1.0 - title: GoDiddy.com - API Reference - contact: - email: contact@danubetech.com -tags: - - name: Universal Resolver - - name: Universal Registrar - - name: Wallet Service - - name: Version Service -paths: - /universal-resolver/identifiers/{identifier}: - get: - summary: Resolve a DID / dereference a DID URL. - description: -

This endpoint resolves a DID. As input it takes the DID, plus DID resolution - options. The output is a DID - document - in one of the supported representations, plus metadata. The same endpoint can also be used to dereference a DID - URL. In this case, the output is a DID document, or part of a DID document, or some other resource identified - by the DID URL.

-

See the DID Resolution - specification for additional details.

- operationId: resolve - tags: - - Universal Resolver - parameters: - - in: path - required: true - name: identifier - schema: - type: string - description: The DID to be resolved, or the DID URL to be dereferenced. - examples: - example1: - value: did:sov:builder:VbPQNHsvoLZdaNU7fTBeFx - description: A DID using the `sov` method. - example2: - value: did:ion:EiClkZMDxPKqC9c-umQfTkR8vvZ9JPhl_xLDI9Nfk38w5w - description: A DID using the `ion` method. - example3: - value: did:ebsi:z25ZZFS7FweHsm9MX2Qvc6gc - description: A DID using the `ebsi` method. - example4: - value: did:sov:builder:VbPQNHsvoLZdaNU7fTBeFx#key-1 - description: A DID URL with a fragment. - example5: - value: did:ebsi:z25ZZFS7FweHsm9MX2Qvc6gc#keys-1 - description: A DID URL with a fragment. - - in: header - required: false - name: Accept - schema: - type: string - description: The requested media type of the DID document representation or DID resolution result. See https://www.w3.org/TR/did-core/#representations and https://w3c-ccg.github.io/did-resolution/#did-resolution-result. - examples: - application/did+json: - value: application/json - description: Media type of a DID document (JSON representation). - application/did+ld+json: - value: application/did+ld+json - description: Media type of a DID document (JSON-LD representation). - application/ld+json;profile="https://w3id.org/did-resolution": - value: application/ld+json;profile="https://w3id.org/did-resolution" - description: Media type of a DID resolution result (JSON-LD representation). - responses: - '200': - description: successfully resolved! - content: - application/did+json: - schema: - type: object - description: The DID document (JSON representation). - example: { - "id": "did:sov:WRfXPg8dantKVubE3HX8pw", - "verificationMethod": [ - { - "id": "did:sov:WRfXPg8dantKVubE3HX8pw#key-1", - "type": "Ed25519VerificationKey2018", - "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" - } - ] - } - application/did+ld+json: - schema: - type: object - description: The DID document (JSON-LD representation). - example: { - "@context": "https://www.w3.org/ns/did/v1", - "id": "did:sov:WRfXPg8dantKVubE3HX8pw", - "verificationMethod": [ - { - "id": "did:sov:WRfXPg8dantKVubE3HX8pw#key-1", - "type": "Ed25519VerificationKey2018", - "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" - } - ] - } - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: '#/components/schemas/UniversalResolver.ResolutionResult' - '400': - description: invalid input! - '410': - description: successfully resolved (deactivated)! - content: - application/did+json: - schema: - type: object - description: The deactivated DID document (JSON representation). - application/did+ld+json: - schema: - type: object - description: The deactivated DID document (JSON-LD representation). - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: '#/components/schemas/UniversalResolver.ResolutionResult' - '500': - description: error! - /universal-resolver/properties: - get: - summary: Retrieve configuration properties. - description: -

This endpoint returns a map of the configuration properties of the DID Resolver, including of its - drivers.

- operationId: universalResolverGetProperties - tags: - - Universal Resolver - responses: - '200': - description: Success. - content: - application/did+json: - schema: - type: object - description: A map of properties. - /universal-resolver/methods: - get: - summary: Retrieve supported DID methods. - description: -

This endpoint returns a list of DID methods supported by the DID Resolver.

- operationId: universalResolverGetMethods - tags: - - Universal Resolver - responses: - '200': - description: Success. - content: - application/did+json: - schema: - type: array - items: - type: string - description: The list of supported DID methods. - example: ["btcr","sov","v1","key"] - /universal-resolver/testIdentifiers: - get: - summary: Retrieve a map of test identifiers. - description: -

This endpoint returns a map of test identifiers that can be resolved by the DID Resolver, - grouped by DID method.

- operationId: universalResolverGetTestIdentifiers - tags: - - Universal Resolver - responses: - '200': - description: Success. - content: - application/did+json: - schema: - type: object - description: A map of test identifiers, grouped by DID method. - example: { "btcr": ["did:btcr:xz35-jznz-q9yu-ply", "did:btcr:xkrn-xz7q-qsye-28p"], "sov": ["did:sov:WRfXPg8dantKVubE3HX8pw"] } - /universal-registrar/create: - post: - summary: Create a DID. - description: -

This endpoint creates a DID. As input it takes the DID, a DID document, plus optional DID registration - options and - secrets - needed to create a DID. The output is a state object that represents the current state of the DID creation - process.

-

See the DID Registration - specification for additional details.

- operationId: create - tags: - - Universal Registrar - parameters: - - in: query - required: true - name: method - schema: - type: string - description: The requested DID method for the operation. - example: key - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.CreateRequest' - responses: - '200': - description: - The request was successful, but the DID may not be fully created yet, as indicated by the - "didState.state" and "jobId" output fields. Additional state information plus metadata are available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.CreateState' - '201': - description: - The DID has been successfully created, as indicated by the "didState.state" output field. - Additional state information (including the created DID) plus metadata are available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.CreateState' - '400': - description: - A problem with the input fields has occurred. Additional state information plus metadata may be - available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.CreateState' - '500': - description: - An internal error has occurred. Additional state information plus metadata may be - available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.CreateState' - /universal-registrar/update: - post: - summary: Update a DID. - description: -

This endpoint updates a DID. As input it takes the existing DID, a DID document, plus optional DID registration - options and - secrets - needed to update a DID. The output is a state object that represents the current state of the DID update - process.

-

See the DID Registration - specification for additional details.

- operationId: update - tags: - - Universal Registrar - parameters: - - in: query - required: true - name: method - schema: - type: string - description: The requested DID method for the operation. - example: btcr - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.UpdateRequest' - responses: - '200': - description: - The request was successful, and the DID may or may not be fully updated yet, as indicated by the - "didState.state" and "jobId" output fields. Additional state information plus metadata are available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.UpdateState' - '400': - description: - A problem with the input fields has occurred. Additional state information plus metadata may be - available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.UpdateState' - '500': - description: - An internal error has occurred. Additional state information plus metadata may be - available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.UpdateState' - /universal-registrar/deactivate: - post: - summary: Deactivate a DID. - description: -

This endpoint deactivates a DID. As input it takes the existing DID, plus optional DID registration - options and - secrets - needed to deactivate a DID. The output is a state object that represents the current state of the DID deactivation - process.

-

See the DID Registration - specification for additional details.

- operationId: deactivate - tags: - - Universal Registrar - parameters: - - in: query - required: true - name: method - schema: - type: string - description: The requested DID method for the operation. - example: btcr - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.DeactivateRequest' - responses: - '200': - description: - The request was successful, and the DID may or may not be fully deactivated yet, as indicated by the - "didState.state" and "jobId" output fields. Additional state information plus metadata are available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.DeactivateState' - '400': - description: - A problem with the input fields has occurred. Additional state information plus metadata may be - available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.DeactivateState' - '500': - description: - An internal error has occurred. Additional state information plus metadata may be - available in the response body. - content: - application/json: - schema: - $ref: '#/components/schemas/UniversalRegistrar.DeactivateState' - /universal-registrar/properties: - get: - summary: Retrieve configuration properties. - description: -

This endpoint returns a map of the configuration properties of the DID Registrar, including of its - drivers.

- operationId: universalRegistrarGetProperties - tags: - - Universal Registrar - responses: - '200': - description: Success. - content: - application/did+json: - schema: - type: object - description: A map of properties. - /universal-registrar/methods: - get: - summary: Retrieve supported DID methods. - description: -

This endpoint returns a list of DID methods supported by the DID Registrar.

- operationId: universalRegistrarGetMethods - tags: - - Universal Registrar - responses: - '200': - description: Success. - content: - application/did+json: - schema: - type: array - items: - type: string - description: The list of supported DID methods. - example: ["btcr","sov","v1","key"] - /wallet-service/keys: - get: - summary: Get key(s) - description: -

This endpoint returns a list of public keys from the wallet service that match the input parameters.

-

Note that there may be multiple keys for a given DID.

- operationId: getKeys - tags: - - Wallet Service - parameters: - - in: query - name: controller - schema: - type: string - format: uri - description: The controller of the key(s) to retrieve. - example: "did:sov:WRfXPg8dantKVubE3HX8pw" - - in: query - name: url - schema: - type: string - format: uri - description: The URL of the key(s) to retrieve. - example: "did:sov:WRfXPg8dantKVubE3HX8pw#keys-1" - - in: query - name: type - schema: - type: string - description: The type of the key(s) to retrieve. - example: "Ed25519" - - in: query - name: purpose - schema: - type: string - description: The purpose(s) of the key(s) to retrieve. - example: "authentication" - - in: query - name: limit - schema: - type: integer - format: int64 - description: The limit (total number) of keys to retrieve. - example: 1 - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/WalletService.Key' - description: The list of key(s). - example: [ - { - "id": "f25bed83-570c-45a4-b2fa-07a0df2a25d5", - "timestamp": 1633531154167, - "controller": "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz", - "url": "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz#z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz", - "type": "Ed25519", - "purpose": "['authentication','assertionMethod','capabilityDelegation','capabilityInvocation']", - "key": { - "kty": "OKP", - "crv": "Ed25519", - "x": "KUnc6ztOeq1mR4UHxY2vmtJQZDk_VikOBTyPvrAu-c8" - }, - "keyMetadata": null - }, - { - "id": "618acb93-0c7d-4a0e-bb78-32745e436404", - "timestamp": 1633531154259, - "controller": "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz", - "url": "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz#z6LStgSrgnA1qWUdiEBcMEduv29jX1n8zmbgw1hcaewouhQV", - "type": "X25519", - "purpose": "['keyAgreement']", - "key": { - "kty": "OKP", - "crv": "X25519", - "x": "_Khr2RW7e-QAHqVx5RjDSshHomTjNDBCLeaJSQklgHI" - }, - "keyMetadata": null - } - ] - description: The request was successful, and the list of public key(s) has been retrieved. - '400': - description: A problem with the input fields has occurred. - '500': - description: An internal error has occurred. - post: - summary: Import key - description: -

This endpoint imports a new private/public key in JWK into the wallet service.

-

Note that when a DID is created using the Universal Registrar, - its keys are automatically imported into the wallet service.

- operationId: importKey - tags: - - Wallet Service - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WalletService.Key' - description: The new key to import. - responses: - '201': - description: The request was successful, and the new key has been imported. - '400': - description: A problem with the input fields has occurred. - '409': - description: This key already exists in the wallet service. - '500': - description: An internal error has occurred. - /wallet-service/keys/{id}: - get: - summary: Get key - description: -

This endpoint returns a private/public key with a given internal identifier from the wallet service.

- operationId: getKey - tags: - - Wallet Service - parameters: - - in: path - name: id - required: true - schema: - type: string - format: uuid - description: The internal identifier of the key to retrieve. - example: "386fd0bf-6bf2-4063-a1b0-42927caf1886" - - in: query - name: exportPrivate - required: false - schema: - type: boolean - description: An optional flag to indicate whether private key data should be included in the response. - example: true - responses: - '200': - description: The request was successful, and the key has been retrieved. - content: - application/json: - schema: - $ref: '#/components/schemas/WalletService.Key' - '400': - description: A problem with the input fields has occurred. - '404': - description: No key was found with the given identifier. - '500': - description: An internal error has occurred. - delete: - summary: Delete key - description: -

This endpoint deletes a private/public key from the wallet service.

-

Note that deleting a key from the wallet service does not deactivate the corresponding DID. This should be - done before deleting keys.

-

Also note that deleting a key may render a DID uncontrollable, unless the key was exported before. - operationId: deleteKey - tags: - - Wallet Service - parameters: - - in: path - name: id - required: true - schema: - type: string - format: uuid - description: The internal identifier of the key to delete. - example: "386fd0bf-6bf2-4063-a1b0-42927caf1886" - responses: - '204': - description: The request was successful, and the key has been deleted. - '400': - description: A problem with the input fields has occurred. - '404': - description: No key was found with the given identifier. - '500': - description: An internal error has occurred. - /wallet-service/keys/sign: - post: - summary: Sign with key - description: -

This endpoint signs an arbitrary unsigned payload using a private key from the wallet service.

- operationId: signWithKey - tags: - - Wallet Service - parameters: - - in: query - name: id - schema: - type: string - format: uuid - description: The internal identifier of the key to use for signing. - example: "386fd0bf-6bf2-4063-a1b0-42927caf1886" - - in: query - name: url - schema: - type: string - format: uri - description: The URL of the key to use for signing. - example: "did:sov:WRfXPg8dantKVubE3HX8pw#keys-1" - - in: query - name: algorithm - schema: - type: string - description: The cryptographic algorithm (JWA) to use for signing. - example: "EdDSA" - requestBody: - content: - application/octet-stream: - schema: - type: string - format: byte - description: The payload to sign. - responses: - '200': - content: - application/octet-stream: - schema: - type: string - format: byte - description: The request was successful, and the payload has been signed. - '400': - description: A problem with the input fields has occurred. - '404': - description: No key was found with the given identifier. - '500': - description: An internal error has occurred. - /wallet-service/keys/verify: - post: - summary: Verify with key - description: -

This endpoint verifies an arbitrary signed payload using a public key from the wallet service.

- operationId: verifyWithKey - tags: - - Wallet Service - parameters: - - in: query - name: id - schema: - type: string - format: uuid - description: The internal identifier of the key to use for verifying. - example: "386fd0bf-6bf2-4063-a1b0-42927caf1886" - - in: query - name: url - schema: - type: string - format: uri - description: The URL of the key to use for verifying. - example: "did:sov:WRfXPg8dantKVubE3HX8pw#keys-1" - requestBody: - content: - application/octet-stream: - schema: - type: string - format: byte - description: The payload to verify. - responses: - '200': - content: - application/json: - schema: - type: object - description: The request was successful, and the payload has been verified. - '400': - description: A problem with the input fields has occurred. - '404': - description: No key was found with the given identifier. - '500': - description: An internal error has occurred. - /wallet-service/controllers: - get: - summary: Get controller(s) - description: -

This endpoint returns a list of controllers from the wallet that match the input parameters.

- operationId: getControllers - parameters: - - in: query - name: type - schema: - type: string - description: The type of the controller(s) to retrieve. - example: "Ed25519" - - in: query - name: purpose - schema: - type: string - description: The purpose(s) of the controller(s) to retrieve. - example: "authentication" - - in: query - name: limit - schema: - type: integer - format: int64 - description: The limit (total number) of controllers to retrieve. - example: 1 - responses: - '200': - content: - application/json: - schema: - type: array - items: - type: string - description: The list of controller(s). - example: [ - "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz", - "did:key:z6MkiD3mSCJZFefR9GXY3bwa6cdxkLyUJ6a7HRFnU3YPNDp2", - "did:sov:danube:C7EGTrTaUruV98HPoEFP9n", - "did:v1:test:nym:z6MknvTVgWvRUHt5wcFQzRSbTReN95cKwo3EAbd1vEmUzUtt" - ] - description: The request was successful, and the list of controller(s) has been retrieved. - '400': - description: A problem with the input fields has occurred. - '404': - description: No controllers were found that match the input parameters. - '500': - description: An internal error has occurred. - /version-service/didrecords: - get: - summary: Get DID Record(s). - description: This endpoint gets matching DID Records. As input, it takes multiple optional parameters. The output is an array of DID Records if there are matches, empty otherwise.

A DID Record represents a DID resolution at a specific point in time, with additional metadata attached to it. Results are sorted by versionTime in descending order. **limit** and **offset** parameters can be used in possible combinations unless stated otherwise. - operationId: getDidRecords - tags: - - Version Service - parameters: - - in: query - name: id - schema: - type: string - format: uuid - example: 8ff29f08-ede2-426d-b127-b95c3fa43192 - description: Unique id of the DID Record to get. When this parameter is set, no other parameter is allowed. - - in: query - name: did - schema: - type: string - format: did - example: did:ion:test:EiDnCmF6bjRF4GEB49PwGi3_AuOmXXBsSV4vor1fkNQc2w - description: A DID to get its DID Records. This parameter can be combined with **versionTime** to get DID Records up until that time or with **versionTimeFrom** and **versionTimeTo** to get DID Records in a given interval. - - in: query - name: method - schema: - type: string - example: jolo - description: A DID method to get its records. This parameter can be combined with **versionTime** to get DID Records of the given method up to (inclusive) the given time. - - in: query - name: versionId - schema: - type: string - example: 22 - description: Gets the DID Record of given **did** with given versionId. Usage only allowed with combination of the **did** field. No other parameters are allowed. - - in: query - name: versionTime - schema: - type: integer - format: int64 - example: 1610582180000 - description: Time in milliseconds. When used alone, this gets the records up to (inclusive) given time. - - in: query - name: versionTimeFrom - schema: - type: integer - format: int64 - description: Epoch time in milliseconds. **versionTime** must be null and **versionTimeTo** must be given to use time-range function. - example: 1610582180000 - - in: query - name: versionTimeTo - schema: - type: integer - format: int64 - example: 1610582180000 - description: Epoch time in milliseconds. **versionTime** must be null and **versionTimeFrom** must be given to use time-range function. - - in: query - name: offset - schema: - type: integer - description: The number of items to skip before starting to collect the result set. - - in: query - name: limit - schema: - type: integer - description: Maximum number of items to return. - responses: - '200': - description: DID record(s) successfully retrieved! - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VersionService.DidRecord' - description: The DID record(s). - '400': - description: invalid input! - '404': - description: DID record(s) not found! - '500': - description: error! - /version-service/didrecords/findInIdentifier: - get: - summary: Find DID records(s) matching identifiers with given term - description: This endpoint searches the given term in DID Records' identifiers, i.e., DIDs. - operationId: findInIdentifier - tags: - - Version Service - parameters: - - in: query - name: term - required: true - schema: - type: string - example: abc - - in: query - name: offset - schema: - type: integer - description: The number of items to skip before starting to collect the result set. - - in: query - name: limit - schema: - type: integer - description: Maximum number of items to return. - responses: - '200': - description: DID Record(s) successfully retrieved! - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VersionService.DidRecord' - description: The DID Record(s). - '400': - description: invalid input! - '404': - description: DID record(s) not found! - '500': - description: error! - /version-service/didrecords/methods: - get: - summary: Get a list of DID methods supported by the Version Service. - operationId: methods - tags: - - Version Service - responses: - '200': - description: DID Method list is successfully retrieved! - content: - application/json: - schema: - type: array - items: - type: string - '500': - description: error! - /version-service/didrecords/count: - get: - summary: Get a number of DID methods matching to given method. - operationId: countByMethod - parameters: - - in: query - name: method - schema: - type: string - responses: - '200': - description: Number of the DID Records for given method is retrieved! - content: - application/json: - schema: - type: integer - format: int64 - description: The matching did record count for the given method. - '500': - description: error! - /version-service/diddocuments: - get: - summary: Get DID document(s) - operationId: getDidDocuments - tags: - - Version Service - parameters: - - in: query - name: id - schema: - type: string - format: uuid - - in: query - name: did - schema: - type: string - format: did - - in: query - name: method - schema: - type: string - - in: query - name: offset - schema: - type: integer - description: The number of items to skip before starting to collect the result set. - - in: query - name: limit - schema: - type: integer - description: Maximum number of items to return. - responses: - '200': - description: DID document(s) successfully retrieved! - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VersionService.DidDocument' - description: The DID document(s). - '400': - description: invalid input! - '404': - description: DID document(s) not found! - '500': - description: error! - /version-service/diddocuments/count: - get: - summary: Count DID document(s) matching to given path-value - operationId: countWithinDidDocuments - tags: - - Version Service - parameters: - - in: query - name: path - schema: - type: array - items: - type: string - - in: query - name: value - schema: - type: object - responses: - '200': - description: DID document(s) successfully retrieved! - content: - application/json: - schema: - type: integer - format: int64 - description: The matching did document count. - '400': - description: invalid input! - '404': - description: DID document(s) not found! - '500': - description: error! - /version-service/diddocuments/search: - get: - summary: Find DID document(s) matching to given path-value - operationId: findInDidDocumentContents - tags: - - Version Service - parameters: - - in: query - name: path - schema: - type: array - items: - type: string - - in: query - name: value - schema: - type: object - - in: query - name: offset - schema: - type: integer - description: The number of items to skip before starting to collect the result set. - - in: query - name: limit - schema: - type: integer - description: Maximum number of items to return. - responses: - '200': - description: DID document(s) successfully retrieved! - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VersionService.DidDocument' - description: The DID document(s). - '400': - description: invalid input! - '404': - description: DID document(s) not found! - '500': - description: error! - /version-service/diddocuments/metadata/count: - get: - summary: Count DID document metadata matching to given path-value - operationId: countWithinDidDocumentMetadata - tags: - - Version Service - parameters: - - in: query - name: path - schema: - type: array - items: - type: string - - in: query - name: value - schema: - type: object - responses: - '200': - description: DID document(s) successfully retrieved! - content: - application/json: - schema: - type: integer - format: int64 - description: The matching did document metadata count. - '400': - description: invalid input! - '404': - description: DID document(s) not found! - '500': - description: error! - /version-service/diddocuments/metadata/search: - get: - summary: Find DID document metadata matching to given path-value - operationId: findInDidDocumentMetadata - tags: - - Version Service - parameters: - - in: query - name: path - schema: - type: array - items: - type: string - - in: query - name: value - schema: - type: object - - in: query - name: offset - schema: - type: integer - description: The number of items to skip before starting to collect the result set. - - in: query - name: limit - schema: - type: integer - description: Maximum number of items to return. - - in: query - name: sort - schema: - type: boolean - description: Sort descending according to record time, ignored in full-text search. - responses: - '200': - description: DID document(s) successfully retrieved! - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/VersionService.DidDocument' - description: The DID document(s). - '400': - description: invalid input! - '404': - description: DID document(s) not found! - '500': - description: error! -components: - schemas: - UniversalResolver.ResolutionResult: - description: The DID resolution result. - type: object - additionalProperties: false - properties: - didDocument: - type: object - example: { - "@context": "https://www.w3.org/ns/did/v1", - "id": "did:sov:WRfXPg8dantKVubE3HX8pw", - "verificationMethod": [ - { - "id": "did:sov:WRfXPg8dantKVubE3HX8pw#key-1", - "type": "Ed25519VerificationKey2018", - "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" - } - ] - } - didResolutionMetadata: - type: object - didDocumentMetadata: - type: object - UniversalRegistrar.CreateRequest: - description: Input fields for the create operation. - type: object - additionalProperties: false - properties: - jobId: - description: - This input field is used to keep track of an ongoing DID creation process. - See https://identity.foundation/did-registration/#jobid. - type: string - example: 6d85bcd0-2ea3-4288-ab00-15afadd8a156 - options: - description: - This input field contains an object with various options for the DID create operation, such as the network where the DID should be created. - See https://identity.foundation/did-registration/#options. - type: object - example: {"chain": "testnet"} - secret: - description: - This input field contains an object with DID controller keys and other secrets needed for performing the DID create operation. - See https://identity.foundation/did-registration/#secret. - type: object - example: {"seed": "72WGp7NgFR1Oqdi8zlt7jQQ434XR0cNQ"} - didDocument: - description: - This input field contains either a complete DID document, or an incremental change (diff) to a DID document. - See https://identity.foundation/did-registration/#diddocument. - type: object - UniversalRegistrar.UpdateRequest: - description: Input fields for the update operation. - type: object - additionalProperties: false - required: - - did - properties: - jobId: - description: - This input field is used to keep track of an ongoing DID update process. - See https://identity.foundation/did-registration/#jobid. - type: string - example: 6d85bcd0-2ea3-4288-ab00-15afadd8a156 - did: - description: - This input field indicates the DID that is the target of the DID update operation. - type: string - example: "did:sov:WRfXPg8dantKVubE3HX8pw" - options: - description: - This input field contains an object with various options for the DID update operation. - See https://identity.foundation/did-registration/#options. - type: object - secret: - description: - This input field contains an object with DID controller keys and other secrets needed for performing the DID update operation. - See https://identity.foundation/did-registration/#secret. - type: object - didDocumentOperation: - description: - This input field indicates which update operation should be applied to a DID’s associated DID document. - See https://identity.foundation/did-registration/#diddocumentoperation. - type: array - items: - type: string - didDocument: - description: - This input field contains either a complete DID document, or an incremental change (diff) to a DID document. - See https://identity.foundation/did-registration/#diddocument. - type: array - items: - type: object - UniversalRegistrar.DeactivateRequest: - description: Input fields for the deactivate operation. - type: object - additionalProperties: false - required: - - did - properties: - jobId: - description: - This input field is used to keep track of an ongoing DID deactivation process. - See https://identity.foundation/did-registration/#jobid. - type: string - example: 6d85bcd0-2ea3-4288-ab00-15afadd8a156 - did: - description: - This input field indicates the DID that is the target of the DID deactivation operation. - type: string - example: "did:sov:WRfXPg8dantKVubE3HX8pw" - options: - description: - This input field contains an object with various options for the DID deactivate operation. - See https://identity.foundation/did-registration/#options. - type: object - secret: - description: - This input field contains an object with DID controller keys and other secrets needed for performing the DID deactivate operation. - See https://identity.foundation/did-registration/#secret. - type: object - UniversalRegistrar.CreateState: - description: The state after a create operation. - type: object - additionalProperties: false - required: - - didState - properties: - jobId: - type: string - example: 6d85bcd0-2ea3-4288-ab00-15afadd8a156 - didState: - $ref: '#/components/schemas/UniversalRegistrar.DidState' - didRegistrationMetadata: - type: object - didDocumentMetadata: - type: object - UniversalRegistrar.UpdateState: - description: The state after an update operation. - type: object - additionalProperties: false - required: - - didState - properties: - jobId: - type: string - example: 6d85bcd0-2ea3-4288-ab00-15afadd8a156 - didState: - $ref: '#/components/schemas/UniversalRegistrar.DidState' - didRegistrationMetadata: - type: object - didDocumentMetadata: - type: object - UniversalRegistrar.DeactivateState: - description: The state after a deactivate operation. - type: object - additionalProperties: false - required: - - didState - properties: - jobId: - type: string - example: 6d85bcd0-2ea3-4288-ab00-15afadd8a156 - didState: - $ref: '#/components/schemas/UniversalRegistrar.DidState' - didRegistrationMetadata: - type: object - didDocumentMetadata: - type: object - UniversalRegistrar.DidState: - description: The current state of a DID. - type: object - additionalProperties: true - properties: - state: - type: string - example: "finished" - did: - type: string - example: "did:sov:WRfXPg8dantKVubE3HX8pw" - secret: - type: object - example: { - "verificationMethod": [{ - "id": "did:sov:WRfXPg8dantKVubE3HX8pw#key-1", - "type": "JsonWebKey2020", - "privateKeyJwk": { - "kty": "OKP", - "d": "NzJXR3A3TmdGUjFPcWRpOHpsdDdqUVE0MzRYUjBjTlE", - "crv": "Ed25519", - "x": "jpIKKU2b77lNXKTNW2NGvw1GUMjU6v_l_tLJAH5uYz0" - } - }] - } - didDocument: - type: object - WalletService.Key: - type: object - required: - - type - - key - properties: - id: - type: string - format: uuid - example: "f25bed83-570c-45a4-b2fa-07a0df2a25d5" - timestamp: - type: integer - format: int64 - example: 1633531154167 - controller: - type: string - format: uri - example: "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz" - url: - type: string - format: uri - example: "did:key:z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz#z6MkhES5w7ViszdQZBfKmzgFM8XVXy6DxVgBZzQCJdrcBuGz" - type: - type: string - example: "Ed25519" - purpose: - type: string - example: "['authentication','assertionMethod','capabilityDelegation','capabilityInvocation']" - key: - type: object - example: { - "kty": "OKP", - "crv": "Ed25519", - "x": "KUnc6ztOeq1mR4UHxY2vmtJQZDk_VikOBTyPvrAu-c8" - } - keyMetadata: - type: object - example: { - } - VersionService.DidRecord: - type: object - required: - - did - - method - properties: - id: - type: string - format: uuid - example: 278dad34-e30b-4159-9ac0-ff5ccc0fdd81 - timestamp: - type: integer - format: int64 - example: 1624014333778 - description: Record's epoch time in milliseconds - did: - type: string - format: did - example: did:sov:WRfXPg8dantKVubE3HX8pw - method: - type: string - example: sov - description: DID Method name - didDocument: - $ref: '#/components/schemas/VersionService.DidDocument' - didVersion: - $ref: '#/components/schemas/VersionService.DidVersion' - didState: - $ref: '#/components/schemas/VersionService.DidState' - VersionService.DidDocument: - type: object - required: - - documentContent - properties: - didRecordId: - type: string - format: uuid - example: 8ff29f08-ede2-426d-b127-b95c3fa43192 - did: - type: string - format: did - example: did:sov:WRfXPg8dantKVubE3HX8pw - documentContent: - type: string - format: json - description: Must be in form of json string. - documentMetadata: - type: string - format: json - description: Must be in form of json string. - VersionService.DidVersion: - type: object - properties: - didRecordId: - type: string - format: uuid - example: 8ff29f08-ede2-426d-b127-b95c3fa43192 - did: - type: string - format: did - example: did:sov:WRfXPg8dantKVubE3HX8pw - versionId: - type: string - example: 18 - versionTime: - type: integer - format: int64 - example: 1624014333778 - description: Version's epoch time in milliseconds - versionMetadata: - type: string - format: json - description: Must be in form of json string. - VersionService.DidState: - type: object - required: - - state - properties: - didRecordId: - type: string - format: uuid - example: 8ff29f08-ede2-426d-b127-b95c3fa43192 - did: - type: string - format: did - example: did:sov:WRfXPg8dantKVubE3HX8pw - state: - type: string - example: pending - stateTime: - type: integer - format: int64 - example: 1624014333778 - stateMetadata: - type: string - format: json - description: Must be in form of json string. diff --git a/examples/mt-keycloak-vault/compose.yaml b/examples/mt-keycloak-vault/compose.yaml new file mode 100644 index 0000000000..daf6939b36 --- /dev/null +++ b/examples/mt-keycloak-vault/compose.yaml @@ -0,0 +1,167 @@ +configs: + caddyfile_default: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-default:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-default:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-default:8080 + } + handle_path /vault* { + reverse_proxy vault-default:8200 + } + } +services: + agent-default: + depends_on: + keycloak-init-default: + condition: service_completed_successfully + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-default + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-default + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-default:8080/didcomm + KEYCLOAK_CLIENT_ID: agent + KEYCLOAK_CLIENT_SECRET: agent-secret + KEYCLOAK_ENABLED: 'true' + KEYCLOAK_REALM: identus + KEYCLOAK_URL: http://keycloak-default:8080 + POLLUX_DB_HOST: db-default + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-default:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-default:8080/prism-agent + SECRET_STORAGE_BACKEND: vault + VAULT_ADDR: http://vault-default:8200 + VAULT_TOKEN: admin + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-default: + configs: + - source: caddyfile_default + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + db-default: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_default:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + keycloak-default: + command: + - start-dev + - --features=preview + - --health-enabled=true + - --hostname-url=http://localhost:8080/keycloak + - --hostname-admin-url=http://localhost:8080/keycloak + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + image: quay.io/keycloak/keycloak:23.0.7 + restart: always + keycloak-init-default: + command: + - --glob + - /hurl/*.hurl + - --test + environment: + HURL_KEYCLOAK_ADMIN_PASSWORD: admin + HURL_KEYCLOAK_ADMIN_USER: admin + HURL_KEYCLOAK_BASE_URL: http://keycloak-default:8080 + HURL_KEYCLOAK_CLIENT_ID: agent + HURL_KEYCLOAK_CLIENT_SECRET: agent-secret + HURL_KEYCLOAK_REALM: identus + image: ghcr.io/orange-opensource/hurl:4.2.0 + volumes: + - ../.shared/hurl/simple_realm:/hurl + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + vault-default: + cap_add: + - IPC_LOCK + environment: + VAULT_ADDR: http://0.0.0.0:8200 + VAULT_DEV_ROOT_TOKEN_ID: admin + healthcheck: + interval: 10s + retries: '5' + test: + - CMD + - vault + - status + timeout: 5s + image: hashicorp/vault:1.15.6 + ports: + - 8200:8200 +volumes: + pg_data_default: {} + pg_data_node: {} diff --git a/examples/mt-keycloak-vault/tests/01_create_users.hurl b/examples/mt-keycloak-vault/tests/01_create_users.hurl new file mode 100644 index 0000000000..9a412b9668 --- /dev/null +++ b/examples/mt-keycloak-vault/tests/01_create_users.hurl @@ -0,0 +1,112 @@ +############################## +# Create users +############################## +# Admin login +POST {{ keycloak_base_url }}/realms/master/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: admin-cli +username: {{ keycloak_admin_user }} +password: {{ keycloak_admin_password }} +HTTP 200 +[Captures] +admin_access_token: jsonpath "$.access_token" + +# Create Issuer +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ issuer_username }}", + "firstName": "Alice", + "lastName": "Wonderland", + "enabled": true, + "email": "alice@atalaprism.io", + "credentials": [{"value": "{{ issuer_password }}", "temporary": false}] +} +HTTP 201 + +# Create Holder +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ holder_username }}", + "firstName": "SpongeBob", + "lastName": "SquarePants", + "enabled": true, + "email": "bob@atalaprism.io", + "credentials": [{"value": "{{ holder_password }}", "temporary": false}] +} +HTTP 201 + +# Create Verifier +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ verifier_username }}", + "firstName": "John", + "lastName": "Wick", + "enabled": true, + "email": "john@atalaprism.io", + "credentials": [{"value": "{{ verifier_password }}", "temporary": false}] +} +HTTP 201 + +############################## +# Create Wallets +############################## +# Issuer login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ issuer_username }} +password: {{ issuer_password }} +HTTP 200 +[Captures] +issuer_access_token: jsonpath "$.access_token" + +# Create Issuer wallet +POST {{ agent_url }}/prism-agent/wallets +Authorization: Bearer {{ issuer_access_token }} +{ + "name": "issuer-wallet" +} +HTTP 201 + +# Holder login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ holder_username }} +password: {{ holder_password }} +HTTP 200 +[Captures] +holder_access_token: jsonpath "$.access_token" + +# Create Holder wallet +POST {{ agent_url }}/prism-agent/wallets +Authorization: Bearer {{ holder_access_token }} +{ + "name": "holder-wallet" +} +HTTP 201 + +# Verifier login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ verifier_username }} +password: {{ verifier_password }} +HTTP 200 +[Captures] +verifier_access_token: jsonpath "$.access_token" + +# Create Verifier wallet +POST {{ agent_url }}/prism-agent/wallets +Authorization: Bearer {{ verifier_access_token }} +{ + "name": "verifier-wallet" +} +HTTP 201 diff --git a/examples/mt-keycloak-vault/tests/02_jwt_flow.hurl b/examples/mt-keycloak-vault/tests/02_jwt_flow.hurl new file mode 100644 index 0000000000..c1ceb033b4 --- /dev/null +++ b/examples/mt-keycloak-vault/tests/02_jwt_flow.hurl @@ -0,0 +1,282 @@ +############################## +# Login +############################## +# Issuer login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ issuer_username }} +password: {{ issuer_password }} +HTTP 200 +[Captures] +issuer_access_token: jsonpath "$.access_token" + +# Holder login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ holder_username }} +password: {{ holder_password }} +HTTP 200 +[Captures] +holder_access_token: jsonpath "$.access_token" + +# Verfier login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ verifier_username }} +password: {{ verifier_password }} +HTTP 200 +[Captures] +verifier_access_token: jsonpath "$.access_token" + + +############################## +# Prerequisites +############################## +# Issuer create DID +POST {{ agent_url }}/prism-agent/did-registrar/dids +Authorization: Bearer {{ issuer_access_token }} +{ + "documentTemplate": { + "publicKeys": [ + { + "id": "iss-key", + "purpose": "assertionMethod" + } + ], + "services": [] + } +} +HTTP 201 +[Captures] +issuer_did: jsonpath "$.longFormDid" regex "(did:prism:[a-z0-9]+):.+$" + +# Holder create DID +POST {{ agent_url }}/prism-agent/did-registrar/dids +Authorization: Bearer {{ holder_access_token }} +{ + "documentTemplate": { + "publicKeys": [ + { + "id": "auth-key", + "purpose": "authentication" + } + ], + "services": [] + } +} +HTTP 201 +[Captures] +holder_did: jsonpath "$.longFormDid" regex "(did:prism:[a-z0-9]+):.+$" + +############################## +# Issuance Connection +############################## +# Inviter create connection +POST {{ agent_url }}/prism-agent/connections +Authorization: Bearer {{ issuer_access_token }} +{ + "label": "My Connection" +} +HTTP 201 +[Captures] +raw_invitation: jsonpath "$.invitation.invitationUrl" regex ".*_oob=(.*)$" +issuer_connection_id: jsonpath "$.connectionId" + +# Invitee accept connection +POST {{ agent_url }}/prism-agent/connection-invitations +Authorization: Bearer {{ holder_access_token }} +{ + "invitation": "{{ raw_invitation }}" +} +HTTP 200 +[Captures] +holder_connection_id: jsonpath "$.connectionId" + +# Wait for inviter connection status +GET {{ agent_url }}/prism-agent/connections/{{ issuer_connection_id }} +Authorization: Bearer {{ issuer_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseSent" + +# Wait for invitee connection status +GET {{ agent_url }}/prism-agent/connections/{{ holder_connection_id }} +Authorization: Bearer {{ holder_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseReceived" + +############################## +# Issuance +############################## +# Issuer create credential offer +POST {{ agent_url }}/prism-agent/issue-credentials/credential-offers +Authorization: Bearer {{ issuer_access_token }} +{ + "claims": { + "emailAddress": "alice@wonderland.com", + "givenName": "Alice", + "familyName": "Wonderland" + }, + "credentialFormat": "JWT", + "issuingDID": "{{ issuer_did }}", + "connectionId": "{{ issuer_connection_id }}" +} +HTTP 201 +[Captures] +issuer_cred_record_id: jsonpath "$.recordId" +didcomm_issuing_thid: jsonpath "$.thid" + +# Holder wait for OfferReceived state +GET {{ agent_url }}/prism-agent/issue-credentials/records +Authorization: Bearer {{ holder_access_token }} +[QueryStringParams] +thid: {{ didcomm_issuing_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].protocolState" == "OfferReceived" +[Captures] +holder_cred_record_id: jsonpath "$.contents[0].recordId" + +# Holder accept a credential-offer +POST {{ agent_url }}/prism-agent/issue-credentials/records/{{ holder_cred_record_id }}/accept-offer +Authorization: Bearer {{ holder_access_token }} +{ + "subjectId": "{{ holder_did }}" +} +HTTP 200 + +# Holder wait for CredentialReceived state +GET {{ agent_url }}/prism-agent/issue-credentials/records/{{ holder_cred_record_id }} +Authorization: Bearer {{ holder_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.protocolState" == "CredentialReceived" + +# Issuer wait for CredentialSent state +GET {{ agent_url }}/prism-agent/issue-credentials/records/{{ issuer_cred_record_id }} +Authorization: Bearer {{ issuer_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.protocolState" == "CredentialSent" + +############################## +# Presentation Connection +############################## +# Inviter create connection +POST {{ agent_url }}/prism-agent/connections +Authorization: Bearer {{ verifier_access_token }} +{ + "label": "My Connection" +} +HTTP 201 +[Captures] +raw_invitation: jsonpath "$.invitation.invitationUrl" regex ".*_oob=(.*)$" +verifier_connection_id: jsonpath "$.connectionId" + +# Invitee accept connection +POST {{ agent_url }}/prism-agent/connection-invitations +Authorization: Bearer {{ holder_access_token }} +{ + "invitation": "{{ raw_invitation }}" +} +HTTP 200 +[Captures] +holder_connection_id: jsonpath "$.connectionId" + +# Wait for inviter connection status +GET {{ agent_url }}/prism-agent/connections/{{ verifier_connection_id }} +Authorization: Bearer {{ verifier_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseSent" + +# Wait for invitee connection status +GET {{ agent_url }}/prism-agent/connections/{{ holder_connection_id }} +Authorization: Bearer {{ holder_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseReceived" + +############################## +# Presentation +############################## +# Verifier create presentation request +POST {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ verifier_access_token }} +{ + "connectionId": "{{ verifier_connection_id }}", + "proofs":[], + "options": { + "challenge": "11c91493-01b3-4c4d-ac36-b336bab5bddf", + "domain": "https://prism-verifier.com" + } +} +HTTP 201 +[Captures] +verifier_presentation_id: jsonpath "$.presentationId" +didcomm_presentation_thid: jsonpath "$.thid" + +# Holder wait for RequestReceived state +GET {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ holder_access_token }} +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "RequestReceived" +[Captures] +holder_presentation_id: jsonpath "$.contents[0].presentationId" + +# Holder accept presentation request +PATCH {{ agent_url }}/prism-agent/present-proof/presentations/{{ holder_presentation_id }} +Authorization: Bearer {{ holder_access_token }} +{ + "action": "request-accept", + "proofId": ["{{ holder_cred_record_id }}"] +} +HTTP 200 + +# Holder wait for PresentationSent state +GET {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ holder_access_token }} +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "PresentationSent" + +# Verfiier wait for PresentationVerified state +GET {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ verifier_access_token }} +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "PresentationVerified" diff --git a/examples/mt-keycloak-vault/tests/local b/examples/mt-keycloak-vault/tests/local new file mode 100644 index 0000000000..a376aaf58b --- /dev/null +++ b/examples/mt-keycloak-vault/tests/local @@ -0,0 +1,15 @@ +keycloak_admin_password=admin +keycloak_admin_user=admin +keycloak_base_url=http://localhost:8080/keycloak +keycloak_realm=identus + +agent_url=http://localhost:8080 + +issuer_password=1234 +issuer_username=alice + +holder_password=1234 +holder_username=bob + +verifier_password=1234 +verifier_username=john diff --git a/examples/mt-keycloak/compose.yaml b/examples/mt-keycloak/compose.yaml new file mode 100644 index 0000000000..3ebc37d1ad --- /dev/null +++ b/examples/mt-keycloak/compose.yaml @@ -0,0 +1,148 @@ +configs: + caddyfile_default: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-default:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-default:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-default:8080 + } + handle_path /vault* { + reverse_proxy vault-default:8200 + } + } +services: + agent-default: + depends_on: + keycloak-init-default: + condition: service_completed_successfully + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-default + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-default + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-default:8080/didcomm + KEYCLOAK_CLIENT_ID: agent + KEYCLOAK_CLIENT_SECRET: agent-secret + KEYCLOAK_ENABLED: 'true' + KEYCLOAK_REALM: identus + KEYCLOAK_URL: http://keycloak-default:8080 + POLLUX_DB_HOST: db-default + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-default:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-default:8080/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-default: + configs: + - source: caddyfile_default + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + db-default: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_default:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + keycloak-default: + command: + - start-dev + - --features=preview + - --health-enabled=true + - --hostname-url=http://localhost:8080/keycloak + - --hostname-admin-url=http://localhost:8080/keycloak + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + image: quay.io/keycloak/keycloak:23.0.7 + restart: always + keycloak-init-default: + command: + - --glob + - /hurl/*.hurl + - --test + environment: + HURL_KEYCLOAK_ADMIN_PASSWORD: admin + HURL_KEYCLOAK_ADMIN_USER: admin + HURL_KEYCLOAK_BASE_URL: http://keycloak-default:8080 + HURL_KEYCLOAK_CLIENT_ID: agent + HURL_KEYCLOAK_CLIENT_SECRET: agent-secret + HURL_KEYCLOAK_REALM: identus + image: ghcr.io/orange-opensource/hurl:4.2.0 + volumes: + - ../.shared/hurl/simple_realm:/hurl + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql +volumes: + pg_data_default: {} + pg_data_node: {} diff --git a/examples/mt-keycloak/tests/01_create_users.hurl b/examples/mt-keycloak/tests/01_create_users.hurl new file mode 100644 index 0000000000..9a412b9668 --- /dev/null +++ b/examples/mt-keycloak/tests/01_create_users.hurl @@ -0,0 +1,112 @@ +############################## +# Create users +############################## +# Admin login +POST {{ keycloak_base_url }}/realms/master/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: admin-cli +username: {{ keycloak_admin_user }} +password: {{ keycloak_admin_password }} +HTTP 200 +[Captures] +admin_access_token: jsonpath "$.access_token" + +# Create Issuer +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ issuer_username }}", + "firstName": "Alice", + "lastName": "Wonderland", + "enabled": true, + "email": "alice@atalaprism.io", + "credentials": [{"value": "{{ issuer_password }}", "temporary": false}] +} +HTTP 201 + +# Create Holder +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ holder_username }}", + "firstName": "SpongeBob", + "lastName": "SquarePants", + "enabled": true, + "email": "bob@atalaprism.io", + "credentials": [{"value": "{{ holder_password }}", "temporary": false}] +} +HTTP 201 + +# Create Verifier +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ verifier_username }}", + "firstName": "John", + "lastName": "Wick", + "enabled": true, + "email": "john@atalaprism.io", + "credentials": [{"value": "{{ verifier_password }}", "temporary": false}] +} +HTTP 201 + +############################## +# Create Wallets +############################## +# Issuer login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ issuer_username }} +password: {{ issuer_password }} +HTTP 200 +[Captures] +issuer_access_token: jsonpath "$.access_token" + +# Create Issuer wallet +POST {{ agent_url }}/prism-agent/wallets +Authorization: Bearer {{ issuer_access_token }} +{ + "name": "issuer-wallet" +} +HTTP 201 + +# Holder login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ holder_username }} +password: {{ holder_password }} +HTTP 200 +[Captures] +holder_access_token: jsonpath "$.access_token" + +# Create Holder wallet +POST {{ agent_url }}/prism-agent/wallets +Authorization: Bearer {{ holder_access_token }} +{ + "name": "holder-wallet" +} +HTTP 201 + +# Verifier login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ verifier_username }} +password: {{ verifier_password }} +HTTP 200 +[Captures] +verifier_access_token: jsonpath "$.access_token" + +# Create Verifier wallet +POST {{ agent_url }}/prism-agent/wallets +Authorization: Bearer {{ verifier_access_token }} +{ + "name": "verifier-wallet" +} +HTTP 201 diff --git a/examples/mt-keycloak/tests/02_jwt_flow.hurl b/examples/mt-keycloak/tests/02_jwt_flow.hurl new file mode 100644 index 0000000000..2169d2f777 --- /dev/null +++ b/examples/mt-keycloak/tests/02_jwt_flow.hurl @@ -0,0 +1,296 @@ +############################## +# Login +############################## +# Issuer login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ issuer_username }} +password: {{ issuer_password }} +HTTP 200 +[Captures] +issuer_access_token: jsonpath "$.access_token" + +# Holder login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ holder_username }} +password: {{ holder_password }} +HTTP 200 +[Captures] +holder_access_token: jsonpath "$.access_token" + +# Verfier login +POST {{ keycloak_base_url }}/realms/{{ keycloak_realm }}/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: web-ui +username: {{ verifier_username }} +password: {{ verifier_password }} +HTTP 200 +[Captures] +verifier_access_token: jsonpath "$.access_token" + + +############################## +# Prerequisites +############################## +# Issuer create DID +POST {{ agent_url }}/prism-agent/did-registrar/dids +Authorization: Bearer {{ issuer_access_token }} +{ + "documentTemplate": { + "publicKeys": [ + { + "id": "iss-key", + "purpose": "assertionMethod" + } + ], + "services": [] + } +} +HTTP 201 +[Captures] +issuer_did: jsonpath "$.longFormDid" regex "(did:prism:[a-z0-9]+):.+$" + +# Issuer publish DID +POST {{ agent_url }}/prism-agent/did-registrar/dids/{{ issuer_did }}/publications +Authorization: Bearer {{ issuer_access_token }} +HTTP 202 + +# Issuer wait for DID to be published +GET {{ agent_url }}/prism-agent/did-registrar/dids/{{ issuer_did }} +Authorization: Bearer {{ issuer_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.status" == "PUBLISHED" + +# Holder create DID +POST {{ agent_url }}/prism-agent/did-registrar/dids +Authorization: Bearer {{ holder_access_token }} +{ + "documentTemplate": { + "publicKeys": [ + { + "id": "auth-key", + "purpose": "authentication" + } + ], + "services": [] + } +} +HTTP 201 +[Captures] +holder_did: jsonpath "$.longFormDid" + +############################## +# Issuance Connection +############################## +# Inviter create connection +POST {{ agent_url }}/prism-agent/connections +Authorization: Bearer {{ issuer_access_token }} +{ + "label": "My Connection" +} +HTTP 201 +[Captures] +raw_invitation: jsonpath "$.invitation.invitationUrl" regex ".*_oob=(.*)$" +issuer_connection_id: jsonpath "$.connectionId" + +# Invitee accept connection +POST {{ agent_url }}/prism-agent/connection-invitations +Authorization: Bearer {{ holder_access_token }} +{ + "invitation": "{{ raw_invitation }}" +} +HTTP 200 +[Captures] +holder_connection_id: jsonpath "$.connectionId" + +# Wait for inviter connection status +GET {{ agent_url }}/prism-agent/connections/{{ issuer_connection_id }} +Authorization: Bearer {{ issuer_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseSent" + +# Wait for invitee connection status +GET {{ agent_url }}/prism-agent/connections/{{ holder_connection_id }} +Authorization: Bearer {{ holder_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseReceived" + +############################## +# Issuance +############################## +# Issuer create credential offer +POST {{ agent_url }}/prism-agent/issue-credentials/credential-offers +Authorization: Bearer {{ issuer_access_token }} +{ + "claims": { + "emailAddress": "alice@wonderland.com", + "givenName": "Alice", + "familyName": "Wonderland" + }, + "credentialFormat": "JWT", + "issuingDID": "{{ issuer_did }}", + "connectionId": "{{ issuer_connection_id }}" +} +HTTP 201 +[Captures] +issuer_cred_record_id: jsonpath "$.recordId" +didcomm_issuing_thid: jsonpath "$.thid" + +# Holder wait for OfferReceived state +GET {{ agent_url }}/prism-agent/issue-credentials/records +Authorization: Bearer {{ holder_access_token }} +[QueryStringParams] +thid: {{ didcomm_issuing_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].protocolState" == "OfferReceived" +[Captures] +holder_cred_record_id: jsonpath "$.contents[0].recordId" + +# Holder accept a credential-offer +POST {{ agent_url }}/prism-agent/issue-credentials/records/{{ holder_cred_record_id }}/accept-offer +Authorization: Bearer {{ holder_access_token }} +{ + "subjectId": "{{ holder_did }}" +} +HTTP 200 + +# Holder wait for CredentialReceived state +GET {{ agent_url }}/prism-agent/issue-credentials/records/{{ holder_cred_record_id }} +Authorization: Bearer {{ holder_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.protocolState" == "CredentialReceived" + +# Issuer wait for CredentialSent state +GET {{ agent_url }}/prism-agent/issue-credentials/records/{{ issuer_cred_record_id }} +Authorization: Bearer {{ issuer_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.protocolState" == "CredentialSent" + +############################## +# Presentation Connection +############################## +# Inviter create connection +POST {{ agent_url }}/prism-agent/connections +Authorization: Bearer {{ verifier_access_token }} +{ + "label": "My Connection" +} +HTTP 201 +[Captures] +raw_invitation: jsonpath "$.invitation.invitationUrl" regex ".*_oob=(.*)$" +verifier_connection_id: jsonpath "$.connectionId" + +# Invitee accept connection +POST {{ agent_url }}/prism-agent/connection-invitations +Authorization: Bearer {{ holder_access_token }} +{ + "invitation": "{{ raw_invitation }}" +} +HTTP 200 +[Captures] +holder_connection_id: jsonpath "$.connectionId" + +# Wait for inviter connection status +GET {{ agent_url }}/prism-agent/connections/{{ verifier_connection_id }} +Authorization: Bearer {{ verifier_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseSent" + +# Wait for invitee connection status +GET {{ agent_url }}/prism-agent/connections/{{ holder_connection_id }} +Authorization: Bearer {{ holder_access_token }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseReceived" + +############################## +# Presentation +############################## +# Verifier create presentation request +POST {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ verifier_access_token }} +{ + "connectionId": "{{ verifier_connection_id }}", + "proofs":[], + "options": { + "challenge": "11c91493-01b3-4c4d-ac36-b336bab5bddf", + "domain": "https://prism-verifier.com" + } +} +HTTP 201 +[Captures] +verifier_presentation_id: jsonpath "$.presentationId" +didcomm_presentation_thid: jsonpath "$.thid" + +# Holder wait for RequestReceived state +GET {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ holder_access_token }} +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "RequestReceived" +[Captures] +holder_presentation_id: jsonpath "$.contents[0].presentationId" + +# Holder accept presentation request +PATCH {{ agent_url }}/prism-agent/present-proof/presentations/{{ holder_presentation_id }} +Authorization: Bearer {{ holder_access_token }} +{ + "action": "request-accept", + "proofId": ["{{ holder_cred_record_id }}"] +} +HTTP 200 + +# Holder wait for PresentationSent state +GET {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ holder_access_token }} +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "PresentationSent" + +# Verfiier wait for PresentationVerified state +GET {{ agent_url }}/prism-agent/present-proof/presentations +Authorization: Bearer {{ verifier_access_token }} +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "PresentationVerified" diff --git a/examples/mt-keycloak/tests/local b/examples/mt-keycloak/tests/local new file mode 100644 index 0000000000..a376aaf58b --- /dev/null +++ b/examples/mt-keycloak/tests/local @@ -0,0 +1,15 @@ +keycloak_admin_password=admin +keycloak_admin_user=admin +keycloak_base_url=http://localhost:8080/keycloak +keycloak_realm=identus + +agent_url=http://localhost:8080 + +issuer_password=1234 +issuer_username=alice + +holder_password=1234 +holder_username=bob + +verifier_password=1234 +verifier_username=john diff --git a/examples/mt/compose.yaml b/examples/mt/compose.yaml new file mode 100644 index 0000000000..84e4f7a3d1 --- /dev/null +++ b/examples/mt/compose.yaml @@ -0,0 +1,114 @@ +configs: + caddyfile_default: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-default:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-default:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-default:8080 + } + handle_path /vault* { + reverse_proxy vault-default:8200 + } + } +services: + agent-default: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-default + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'true' + CONNECT_DB_HOST: db-default + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-default:8080/didcomm + POLLUX_DB_HOST: db-default + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-default:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-default:8080/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-default: + configs: + - source: caddyfile_default + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + db-default: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_default:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql +volumes: + pg_data_default: {} + pg_data_node: {} diff --git a/examples/readme.md b/examples/readme.md deleted file mode 100644 index 2344b5197c..0000000000 --- a/examples/readme.md +++ /dev/null @@ -1,79 +0,0 @@ -# Examples of OpenAPI specifications -This directory contains OpenAPI/Swagger specifications of Identus competitors. -Not all the companies are real competitors, but number of them are experts in particular area, so we have a good opportunity to get inspiration and build a better solution. - -### godiddy-api.yaml -[Godiddy](https://godiddy.com/) is a hosted platform that makes it easy for SSI developers and solution providers to work with DIDs -It provides the following functionality: -- Resolve DIDs -- Manage DIDs -- Search DIDs - -`Castor` is going to provide slightly similar functionality - -Points to consider: -- routes are grouped by service name -- path convention: {service-name}/{resource}: `/wallet-service/keys` -- each schema contain a prefix of the service name: `WalletService.Key` -- did methods are `verbs`, DIDs is not a collection. The path for methods looks like `RPC` -- `diddocuments` is a collection of all the `versions` of the DID -- wallet service implementation contains path with verbs: `sign` and `verify` - -### aries-cloud-agent.json -[Aries Cloud Agent]() is the SSI solution from Hyperledger - -Hyperledger Aries Cloud Agent Python (ACA-Py) is a foundation for building Verifiable Credential (VC) ecosystems. It operates in the second and third layers of the Trust Over IP framework (PDF) using DIDComm messaging and Hyperledger Aries protocols. The "cloud" in the name means that ACA-Py runs on servers (cloud, enterprise, IoT devices, and so forth), and is not designed to run on mobile devices. - -`Castor`, `Pollux`, `Mercury` are going to provide similar functionality - -Aries Cloud Agent API is a mix of everything you need for SSI in a single place. - -Pros: -- everything is in the single place -- easy to deploy and integrate - -Cons: -- monolith architecture -- probably, it's hard to evolve it because of coupling -- mix of all the agents for Issuer/Verifier/Holder/Mediator/Other APIs - -Points to consider: -- part of the routes are grouped by entity/protocol/service: `did-exchange`, `action-menu`, `mediation` -- part of the routes are started from the root and it's hard to figure out what exactly you are going to do: look at `/connections` -- mix of resource and verbs for protocol facades which is hard to understand: `present-proof`, `issue-credentials` - -### sipca-essif-bridge-api.json -[Essif Bridge](https://github.com/sicpa-dlab/essif-bridge) - -For the BRIDGE-project, SICPA proposes 3 technological building blocks that will enhance interoperability and scalability in the SSI ecosystem by giving freedom of choice between verifiable credentials exchange protocols (DIDcomm & CHAPI), credential types (JSON-LD & Anoncreds) and DID-methods. - -This API documentation is a good example of the integration solution. - -### trinsic-credentials-v1-resolved.json - -[Trinsic](https://docs.trinsic.id/reference/authentication) - -Trinsic is the proof of anything platform. We make it easy for people and organizations to prove things about themselves with technology instead of paper documents. Our software is based on Decentralized Identifiers (DIDs) and Verifiable Credentials (VCs), a new digital identity standard. We use the open-source Hyperledger Aries project, to which we are a primary contributor. (c) - -Trinsic is a competitor of Atala Prism that provides similar functionality. - -It's focused on commodity wallet solution, uses Hyperledger Aries under the hood, provides Open API spec for all the endpoints and SDK in number of languages. - -`Castor`, `Pollux`, `Mercury` are going to provide similar functionality. -Number of Atala products provides similar functionality - -Pros: -- good REST API design -- one level of resources in REST API -- attention to documentation -- solid and simple models without overloading -- should be easy and build the solutions - -Cons: -- simplicity of solution causes lack of `offline` functionality - -Points to consider: - -- Would be nice to have the same quality of API in Atala Prism -- API specification is much simple and engineer-friendly compared to Aries -- Schemas are intuitive, well described and easy-for-quick-start \ No newline at end of file diff --git a/examples/sipca-essif-bridge-api.json b/examples/sipca-essif-bridge-api.json deleted file mode 100644 index e6dc7d123c..0000000000 --- a/examples/sipca-essif-bridge-api.json +++ /dev/null @@ -1,1426 +0,0 @@ -{ - "openapi": "3.0.1", - "info": { - "title": "Sicpa Bridge API", - "version": "0.0.1" - }, - "servers": [ - { - "url": "/api/" - } - ], - "tags": [ - { - "name": "Verifications", - "description": "The Verifications API" - }, - { - "name": "Schemas", - "description": "The Schemas API" - }, - { - "name": "OpenID Connect Connect", - "description": "OpenID Connect API" - }, - { - "name": "Credential Definitions", - "description": "The Credential Definitions API" - }, - { - "name": "JSON-LD Verification", - "description": "Verify a given credential." - }, - { - "name": "Connections", - "description": "The Connections API" - }, - { - "name": "JSON-LD Credentials", - "description": "Issues a JSON-LD credential and returns it in the response body." - }, - { - "name": "Credentials Issuance", - "description": "The Credentials Issuance API" - } - ], - "paths": { - "/verifications": { - "post": { - "tags": [ - "Verifications" - ], - "summary": "Create a new Verification", - "operationId": "verificationsPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerificationCreate" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Verification" - } - } - } - } - } - } - }, - "/verification-templates": { - "post": { - "tags": [ - "Verifications" - ], - "summary": "Create a new Verification Template", - "operationId": "verificationTemplatesPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerificationTemplateCreate" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerificationTemplate" - } - } - } - } - } - } - }, - "/schemas": { - "post": { - "tags": [ - "Schemas" - ], - "summary": "Create a new schema", - "operationId": "schemaPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SchemaCreate" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SchemaSummary" - } - } - } - } - } - } - }, - "/presentations/verify": { - "post": { - "tags": [ - "JSON-LD Verification" - ], - "operationId": "verityCredential", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiablePresentation" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerificationResult" - } - } - } - } - } - } - }, - "/oidc/sendcredential": { - "post": { - "tags": [ - "OpenID Connect Connect" - ], - "summary": "Sends a credential", - "operationId": "sendCredential", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/oidc/didauthresponse/{clientId}": { - "post": { - "tags": [ - "OpenID Connect Connect" - ], - "summary": "Processes Auth Response", - "operationId": "didAuthResponse", - "parameters": [ - { - "name": "clientId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/AuthResponseCallback" - } - } - } - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/oidc/didauthrequest": { - "post": { - "tags": [ - "OpenID Connect Connect" - ], - "summary": "Create a new authenticate url", - "operationId": "didAuthRequest", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthRequestParam" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthRequestResponse" - } - } - } - } - } - } - }, - "/credentials/verify": { - "post": { - "tags": [ - "JSON-LD Verification" - ], - "operationId": "verityCredential_1", - "requestBody": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/VerifiableCredential" - }, - { - "$ref": "#/components/schemas/VerifiableCredentialMultipleProof" - } - ] - }, - "examples": { - "Verifiable Credential": { - "description": "Verifiable Credential", - "value": { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "id": "http://example.gov/credentials/3732", - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "issuer": "did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd", - "issuanceDate": "2020-03-16T22:37:26.544Z", - "expirationDate": "2020-03-16T22:37:26.544Z", - "credentialSubject": { - "id": "did:example:123", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - }, - "proof": { - "type": "Ed25519Signature2018", - "created": "2020-04-02T18:28:08Z", - "verificationMethod": "did:example:123#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN", - "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA" - } - } - }, - "Multiple Proof Verifiable Credential": { - "description": "Multiple Proof Verifiable Credential", - "value": { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "id": "http://example.gov/credentials/3732", - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "issuer": "did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd", - "issuanceDate": "2020-03-16T22:37:26.544Z", - "expirationDate": "2020-03-16T22:37:26.544Z", - "credentialSubject": { - "id": "did:example:123", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - }, - "proof": [ - { - "type": "Ed25519Signature2018", - "created": "2020-04-02T18:28:08Z", - "verificationMethod": "did:example:123#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN", - "proofPurpose": "assertionMethod", - "jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA" - } - ] - } - } - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Result", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerificationResult" - } - } - } - }, - "400": { - "description": "invalid input!", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "error!", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/credentials/issue": { - "post": { - "tags": [ - "JSON-LD Credentials" - ], - "operationId": "issueCredential", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Credential" - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "Credential successfully issued!", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerifiableCredential" - } - } - } - }, - "400": { - "description": "invalid input!", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "error!", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/credentials-issuance": { - "post": { - "tags": [ - "Credentials Issuance" - ], - "summary": "Issue new credentials", - "operationId": "issuanceCredentialPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CredentialCreate" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CredentialIssuance" - } - } - } - } - } - } - }, - "/credential-definitions": { - "post": { - "tags": [ - "Credential Definitions" - ], - "summary": "Create a new credentialDefinition", - "operationId": "credentialDefinitionPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CredentialDefinitionCreate" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CredentialDefinitionSummary" - } - } - } - } - } - } - }, - "/connections": { - "post": { - "tags": [ - "Connections" - ], - "summary": "Create a new connection invitation", - "operationId": "connectionsPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ConnectionInvitationCreate" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ConnectionInvitationCreationResult" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "VerificationCreate": { - "required": [ - "connectionId", - "verificationTemplateId" - ], - "type": "object", - "properties": { - "verificationTemplateId": { - "type": "integer", - "description": "The verification template id", - "format": "int32", - "example": 0 - }, - "connectionId": { - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", - "type": "string", - "description": "The connection id", - "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" - }, - "comment": { - "type": "string", - "description": "An optional comment" - } - } - }, - "RequestedAttribute": { - "required": [ - "names", - "restrictions" - ], - "type": "object", - "properties": { - "names": { - "type": "array", - "description": "List of attribute names", - "items": { - "type": "string", - "description": "List of attribute names" - } - }, - "restrictions": { - "type": "array", - "description": "The attribute restrictions. At least one restriction should be valid in order for the verification to be successful. (Not considering predicates, revocation requirements...)", - "items": { - "$ref": "#/components/schemas/Restriction" - } - } - }, - "description": "The requested attributes" - }, - "RequestedPredicate": { - "required": [ - "name", - "predicateType", - "predicateValue", - "restrictions" - ], - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "A predicate name" - }, - "predicateType": { - "type": "string", - "description": "A predicate type", - "enum": [ - "LESS_THAN", - "LESS_THAN_OR_EQUAL_TO", - "GREATER_THAN_OR_EQUAL_TO", - "GREATER_THAN" - ] - }, - "predicateValue": { - "type": "integer", - "description": "A predicate value", - "format": "int32" - }, - "restrictions": { - "type": "array", - "description": "A list of predicate restrictions", - "items": { - "$ref": "#/components/schemas/Restriction" - } - } - }, - "description": "The requested predicates" - }, - "Restriction": { - "required": [ - "credentialDefinitionId" - ], - "type": "object", - "properties": { - "credentialDefinitionId": { - "type": "string", - "description": "A credential definition ID" - }, - "schemaId": { - "type": "string", - "description": "A schema ID" - } - }, - "description": "A list of predicate restrictions" - }, - "Verification": { - "required": [ - "connectionId", - "createdAt", - "state", - "updatedAt", - "verificationId", - "verificationRequest" - ], - "type": "object", - "properties": { - "verificationId": { - "type": "string", - "description": "The verification id", - "example": "ac32f24e-2f90-4e90-9427-c59482078a4b" - }, - "connectionId": { - "type": "string", - "description": "The connection id", - "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" - }, - "verificationRequest": { - "$ref": "#/components/schemas/VerificationRequest" - }, - "state": { - "type": "string", - "description": "The verification state", - "enum": [ - "SUCCESS", - "PENDING", - "FAILURE", - "ERROR" - ] - }, - "createdAt": { - "type": "string", - "description": "The creation date ", - "format": "date-time" - }, - "updatedAt": { - "type": "string", - "description": "The update date", - "format": "date-time" - } - } - }, - "VerificationRequest": { - "required": [ - "name", - "requestedAttributes", - "requestedPredicates" - ], - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The verification request name" - }, - "requestedAttributes": { - "type": "array", - "description": "The requested attributes", - "items": { - "$ref": "#/components/schemas/RequestedAttribute" - } - }, - "requestedPredicates": { - "type": "array", - "description": "The requested predicates", - "items": { - "$ref": "#/components/schemas/RequestedPredicate" - } - }, - "validAt": { - "type": "string", - "description": "The date at which the credential should be valid", - "format": "date-time" - } - }, - "description": "The verification request" - }, - "RevocationRequirement": { - "type": "object", - "properties": { - "validAt": { - "type": "string", - "description": "Date at which the credential should be valid", - "format": "date-time" - }, - "validNow": { - "type": "boolean", - "description": "Defines if the credential should be valid at the current date" - } - } - }, - "VerificationTemplateContent": { - "required": [ - "requestedAttributes", - "requestedPredicates" - ], - "type": "object", - "properties": { - "requestedAttributes": { - "type": "array", - "description": "The requested attributes. In order for the verification to be successful, all requested attribute should be validated", - "items": { - "$ref": "#/components/schemas/RequestedAttribute" - } - }, - "requestedPredicates": { - "type": "array", - "description": "The requested predicates. In order for the verification to be successful, all requested predicates should be validated", - "items": { - "$ref": "#/components/schemas/RequestedPredicate" - } - }, - "revocationRequirement": { - "$ref": "#/components/schemas/RevocationRequirement" - } - } - }, - "VerificationTemplateCreate": { - "required": [ - "content", - "name" - ], - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The verificationTemplate name" - }, - "content": { - "$ref": "#/components/schemas/VerificationTemplateContent" - } - } - }, - "VerificationTemplate": { - "required": [ - "content", - "id", - "name" - ], - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "The verificationTemplate id", - "format": "int32" - }, - "name": { - "type": "string", - "description": "The verificationTemplate name", - "example": "example" - }, - "content": { - "$ref": "#/components/schemas/VerificationTemplateContent" - } - } - }, - "SchemaCreate": { - "required": [ - "attributesName", - "name", - "version" - ], - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Schema name", - "example": "prefs" - }, - "version": { - "pattern": "^[0-9.]+$", - "type": "string", - "description": "Schema version", - "example": "1.0" - }, - "attributesName": { - "type": "array", - "description": "List of schema attributes", - "items": { - "type": "string", - "description": "List of schema attributes" - } - } - } - }, - "SchemaSummary": { - "required": [ - "id" - ], - "type": "object", - "properties": { - "id": { - "pattern": "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", - "type": "string", - "description": "The schema's id" - } - } - }, - "LinkedDataProof": { - "required": [ - "created", - "jws", - "proofPurpose", - "type", - "verificationMethod" - ], - "type": "object", - "properties": { - "type": { - "type": "string", - "description": "Linked Data Signature Suite used to produce proof.", - "example": "Ed25519Signature2018" - }, - "created": { - "type": "string", - "description": "Date the proof was created.", - "example": "2020-04-02T18:28:08Z" - }, - "verificationMethod": { - "type": "string", - "description": "Verification Method used to verify proof.", - "example": "did:example:123#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN" - }, - "proofPurpose": { - "type": "string", - "description": "The purpose of the proof to be used with verificationMethod.", - "example": "assertionMethod" - }, - "jws": { - "type": "string", - "description": "Detached JSON Web Signature", - "example": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA" - }, - "cades": { - "type": "string" - } - }, - "description": "A JSON-LD Linked Data proof." - }, - "VerifiableCredential": { - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" - ], - "type": "object", - "properties": { - "@context": { - "type": "array", - "description": "The JSON-LD context of the credential.", - "example": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "items": { - "type": "string", - "description": "The JSON-LD context of the credential.", - "example": "[\"https://www.w3.org/2018/credentials/v1\",\"https://www.w3.org/2018/credentials/examples/v1\"]" - } - }, - "id": { - "type": "string", - "description": "The ID of the credential.", - "nullable": true, - "example": "http://example.gov/credentials/3732" - }, - "type": { - "type": "array", - "description": "The JSON-LD type of the credential.", - "example": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "items": { - "type": "string", - "description": "The JSON-LD type of the credential.", - "example": "[\"VerifiableCredential\",\"UniversityDegreeCredential\"]" - } - }, - "issuer": { - "type": "string", - "description": "A JSON-LD Verifiable com.sicpa.bridge.api.jsonld.domain.model.Credential Issuer.", - "example": "did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd" - }, - "issuanceDate": { - "type": "string", - "description": "The issuanceDate", - "example": "2020-03-16T22:37:26.544Z" - }, - "expirationDate": { - "type": "string", - "description": "The expirationDate", - "nullable": true, - "example": "2020-03-16T22:37:26.544Z" - }, - "credentialSubject": { - "type": "object", - "description": "The subject", - "example": { - "id": "did:example:123", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - } - }, - "proof": { - "$ref": "#/components/schemas/LinkedDataProof" - } - } - }, - "VerifiablePresentation": { - "required": [ - "@context", - "holder", - "proof", - "type", - "verifiableCredential" - ], - "type": "object", - "properties": { - "@context": { - "type": "array", - "description": "The JSON-LD context of the presentation.", - "example": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "items": { - "type": "string", - "description": "The JSON-LD context of the presentation.", - "example": "[\"https://www.w3.org/2018/credentials/v1\",\"https://www.w3.org/2018/credentials/examples/v1\"]" - } - }, - "holder": { - "type": "string", - "description": "The holder", - "example": "did:example:123" - }, - "type": { - "type": "string", - "description": "The JSON-LD type of the presentation.", - "example": "VerifiablePresentation" - }, - "verifiableCredential": { - "$ref": "#/components/schemas/VerifiableCredential" - }, - "proof": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LinkedDataProof" - } - } - } - }, - "VerificationResult": { - "required": [ - "checks", - "errors", - "warnings" - ], - "type": "object", - "properties": { - "checks": { - "type": "array", - "description": "The checks performed", - "example": [ - "proof" - ], - "items": { - "type": "string", - "description": "The checks performed", - "example": "[\"proof\"]" - } - }, - "warnings": { - "type": "array", - "description": "Warnings", - "example": [], - "items": { - "type": "string", - "description": "Warnings", - "example": "[]" - } - }, - "errors": { - "type": "array", - "description": "Errors", - "example": [], - "items": { - "type": "string", - "description": "Errors", - "example": "[]" - } - }, - "info": { - "type": "object" - } - } - }, - "AuthResponseCallback": { - "required": [ - "id_token", - "login_challenge", - "state" - ], - "type": "object", - "properties": { - "id_token": { - "type": "string" - }, - "state": { - "type": "string" - }, - "login_challenge": { - "type": "string" - } - } - }, - "AuthRequestParam": { - "required": [ - "clientId", - "credentialId" - ], - "type": "object", - "properties": { - "clientId": { - "type": "string" - }, - "credentialId": { - "type": "string" - } - } - }, - "AuthRequestResponse": { - "required": [ - "url" - ], - "type": "object", - "properties": { - "url": { - "type": "string" - } - } - }, - "VerifiableCredentialMultipleProof": { - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" - ], - "type": "object", - "properties": { - "@context": { - "type": "array", - "description": "The JSON-LD context of the credential.", - "example": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "items": { - "type": "string", - "description": "The JSON-LD context of the credential.", - "example": "[\"https://www.w3.org/2018/credentials/v1\",\"https://www.w3.org/2018/credentials/examples/v1\"]" - } - }, - "id": { - "type": "string", - "description": "The ID of the credential.", - "nullable": true, - "example": "http://example.gov/credentials/3732" - }, - "type": { - "type": "array", - "description": "The JSON-LD type of the credential.", - "example": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "items": { - "type": "string", - "description": "The JSON-LD type of the credential.", - "example": "[\"VerifiableCredential\",\"UniversityDegreeCredential\"]" - } - }, - "issuer": { - "type": "string", - "description": "A JSON-LD Verifiable com.sicpa.bridge.api.jsonld.domain.model.Credential Issuer.", - "example": "did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd" - }, - "issuanceDate": { - "type": "string", - "description": "The issuanceDate", - "example": "2020-03-16T22:37:26.544Z" - }, - "expirationDate": { - "type": "string", - "description": "The expirationDate", - "nullable": true, - "example": "2020-03-16T22:37:26.544Z" - }, - "credentialSubject": { - "type": "object", - "description": "The subject", - "example": { - "id": "did:example:123", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - } - }, - "proof": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LinkedDataProof" - } - } - } - }, - "Credential": { - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "type" - ], - "type": "object", - "properties": { - "@context": { - "type": "array", - "description": "The JSON-LD context of the credential.", - "example": [ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1" - ], - "items": { - "type": "string", - "description": "The JSON-LD context of the credential.", - "example": "[\"https://www.w3.org/2018/credentials/v1\",\"https://www.w3.org/2018/credentials/examples/v1\"]" - } - }, - "id": { - "type": "string", - "description": "The ID of the credential.", - "nullable": true, - "example": "http://example.gov/credentials/3732" - }, - "type": { - "type": "array", - "description": "The JSON-LD type of the credential.", - "example": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ], - "items": { - "type": "string", - "description": "The JSON-LD type of the credential.", - "example": "[\"VerifiableCredential\",\"UniversityDegreeCredential\"]" - } - }, - "issuer": { - "type": "string", - "description": "A JSON-LD Verifiable com.sicpa.bridge.api.jsonld.domain.model.Credential Issuer.", - "example": "did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd" - }, - "issuanceDate": { - "type": "string", - "description": "The issuanceDate", - "example": "2020-03-16T22:37:26.544Z" - }, - "credentialSubject": { - "type": "object", - "description": "The subject", - "example": { - "id": "did:example:123", - "degree": { - "type": "BachelorDegree", - "name": "Bachelor of Science and Arts" - } - } - } - } - }, - "CredentialAttribute": { - "required": [ - "name", - "value" - ], - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "A credential attribute name" - }, - "value": { - "type": "string", - "description": "A credential attribute value" - } - }, - "description": "A list of credential attributes" - }, - "CredentialCreate": { - "required": [ - "attributes", - "credentialDefinitionId", - "schemaId" - ], - "type": "object", - "properties": { - "connectionId": { - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", - "type": "string", - "description": "A connection ID", - "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6" - }, - "schemaId": { - "pattern": "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", - "type": "string", - "description": "A schema ID" - }, - "credentialDefinitionId": { - "pattern": "([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", - "type": "string", - "description": "A credential definition ID" - }, - "comment": { - "type": "string", - "description": "A human readable comment" - }, - "attributes": { - "type": "array", - "description": "A list of credential attributes", - "items": { - "$ref": "#/components/schemas/CredentialAttribute" - } - } - } - }, - "CredentialIssuance": { - "required": [ - "credential", - "issuanceId", - "state" - ], - "type": "object", - "properties": { - "issuanceId": { - "type": "string", - "description": "A credential issuance ID" - }, - "errorMessage": { - "type": "string", - "description": "Optional error message" - }, - "state": { - "type": "string", - "description": "State of the credential transaction" - }, - "updatedAt": { - "type": "string", - "format": "date-time" - }, - "credential": { - "$ref": "#/components/schemas/Credential" - } - } - }, - "CredentialDefinitionCreate": { - "required": [ - "schemaId", - "supportRevocation", - "tag" - ], - "type": "object", - "properties": { - "schemaId": { - "pattern": "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+$", - "type": "string", - "description": "Schema identifier", - "example": "WgWxqztrNooG92RXvxSTWv:2:schema_name:1.0" - }, - "tag": { - "type": "string", - "description": "Credential definition identifier tag", - "example": "default" - }, - "supportRevocation": { - "type": "boolean", - "description": "Revocation supported flag" - } - } - }, - "CredentialDefinitionSummary": { - "required": [ - "id" - ], - "type": "object", - "properties": { - "id": { - "pattern": "([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}):3:CL:(([1-9][0-9]*)|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21,22}:2:.+:[0-9.]+)):(.+)?$", - "type": "string", - "description": "The credential definition's id" - } - } - }, - "ConnectionInvitationCreate": { - "type": "object", - "properties": { - "alias": { - "type": "string", - "description": "Connection alias", - "example": "Some alias" - } - } - }, - "ConnectionInvitationContent": { - "required": [ - "invitationId", - "recipientKeys", - "serviceEndpoint" - ], - "type": "object", - "properties": { - "invitationId": { - "type": "string", - "description": "The connection's invitation id" - }, - "serviceEndpoint": { - "type": "string", - "description": "The connection's service endpoint" - }, - "recipientKeys": { - "type": "array", - "description": "The list of recipient's keys", - "items": { - "type": "string", - "description": "The list of recipient's keys" - } - }, - "label": { - "type": "string", - "description": "The connection's label" - } - } - }, - "ConnectionInvitationCreationResult": { - "required": [ - "connectionId", - "connectionInvitationContent", - "invitationUrl" - ], - "type": "object", - "properties": { - "invitationUrl": { - "type": "string", - "description": "Invitation URL", - "example": "http://192.168.56.101:8020/invite?c_i=eyJAdHlwZSI6Li4ufQ==" - }, - "connectionInvitationContent": { - "$ref": "#/components/schemas/ConnectionInvitationContent" - }, - "connectionId": { - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", - "type": "string" - } - } - } - } - } -} \ No newline at end of file diff --git a/examples/st-multi/compose.yaml b/examples/st-multi/compose.yaml new file mode 100644 index 0000000000..34650ad413 --- /dev/null +++ b/examples/st-multi/compose.yaml @@ -0,0 +1,268 @@ +configs: + caddyfile_holder: + content: |- + :8081 { + handle_path /didcomm* { + reverse_proxy agent-holder:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-holder:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-holder:8080 + } + handle_path /vault* { + reverse_proxy vault-holder:8200 + } + } + caddyfile_issuer: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-issuer:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-issuer:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-issuer:8080 + } + handle_path /vault* { + reverse_proxy vault-issuer:8200 + } + } + caddyfile_verifier: + content: |- + :8082 { + handle_path /didcomm* { + reverse_proxy agent-verifier:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-verifier:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-verifier:8080 + } + handle_path /vault* { + reverse_proxy vault-verifier:8200 + } + } +services: + agent-holder: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-holder + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-holder + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-holder:8081/didcomm + POLLUX_DB_HOST: db-holder + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-holder:8081/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-holder:8081/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + agent-issuer: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-issuer + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-issuer + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-issuer:8080/didcomm + POLLUX_DB_HOST: db-issuer + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-issuer:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-issuer:8080/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + agent-verifier: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-verifier + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-verifier + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-verifier:8082/didcomm + POLLUX_DB_HOST: db-verifier + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-verifier:8082/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-verifier:8082/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-holder: + configs: + - source: caddyfile_holder + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8081:8081 + restart: always + caddy-issuer: + configs: + - source: caddyfile_issuer + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + caddy-verifier: + configs: + - source: caddyfile_verifier + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8082:8082 + restart: always + db-holder: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_holder:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + db-issuer: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_issuer:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + db-verifier: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_verifier:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql +volumes: + pg_data_holder: {} + pg_data_issuer: {} + pg_data_node: {} + pg_data_verifier: {} diff --git a/examples/st-multi/tests/01_jwt_flow.hurl b/examples/st-multi/tests/01_jwt_flow.hurl new file mode 100644 index 0000000000..94bc540275 --- /dev/null +++ b/examples/st-multi/tests/01_jwt_flow.hurl @@ -0,0 +1,237 @@ +############################## +# Prerequisites +############################## +# Issuer create DID +POST {{ issuer_url }}/prism-agent/did-registrar/dids +{ + "documentTemplate": { + "publicKeys": [ + { + "id": "iss-key", + "purpose": "assertionMethod" + } + ], + "services": [] + } +} +HTTP 201 +[Captures] +issuer_did: jsonpath "$.longFormDid" regex "(did:prism:[a-z0-9]+):.+$" + +# Issuer publish DID +POST {{ issuer_url }}/prism-agent/did-registrar/dids/{{ issuer_did }}/publications +HTTP 202 + +# Issuer wait for DID to be published +GET {{ issuer_url }}/prism-agent/did-registrar/dids/{{ issuer_did }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.status" == "PUBLISHED" + +# Holder create DID +POST {{ holder_url }}/prism-agent/did-registrar/dids +{ + "documentTemplate": { + "publicKeys": [ + { + "id": "auth-key", + "purpose": "authentication" + } + ], + "services": [] + } +} +HTTP 201 +[Captures] +holder_did: jsonpath "$.longFormDid" + +############################## +# Issuance Connection +############################## +# Inviter create connection +POST {{ issuer_url }}/prism-agent/connections +{ + "label": "My Connection" +} +HTTP 201 +[Captures] +raw_invitation: jsonpath "$.invitation.invitationUrl" regex ".*_oob=(.*)$" +issuer_connection_id: jsonpath "$.connectionId" + +# Invitee accept connection +POST {{ holder_url }}/prism-agent/connection-invitations +{ + "invitation": "{{ raw_invitation }}" +} +HTTP 200 +[Captures] +holder_connection_id: jsonpath "$.connectionId" + +# Wait for inviter connection status +GET {{ issuer_url }}/prism-agent/connections/{{ issuer_connection_id }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseSent" + +# Wait for invitee connection status +GET {{ holder_url }}/prism-agent/connections/{{ holder_connection_id }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseReceived" + +############################## +# Issuance +############################## +# Issuer create credential offer +POST {{ issuer_url }}/prism-agent/issue-credentials/credential-offers +{ + "claims": { + "emailAddress": "alice@wonderland.com", + "givenName": "Alice", + "familyName": "Wonderland" + }, + "credentialFormat": "JWT", + "issuingDID": "{{ issuer_did }}", + "connectionId": "{{ issuer_connection_id }}" +} +HTTP 201 +[Captures] +issuer_cred_record_id: jsonpath "$.recordId" +didcomm_issuing_thid: jsonpath "$.thid" + +# Holder wait for OfferReceived state +GET {{ holder_url }}/prism-agent/issue-credentials/records +[QueryStringParams] +thid: {{ didcomm_issuing_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].protocolState" == "OfferReceived" +[Captures] +holder_cred_record_id: jsonpath "$.contents[0].recordId" + +# Holder accept a credential-offer +POST {{ holder_url }}/prism-agent/issue-credentials/records/{{ holder_cred_record_id }}/accept-offer +{ + "subjectId": "{{ holder_did }}" +} +HTTP 200 + +# Holder wait for CredentialReceived state +GET {{ holder_url }}/prism-agent/issue-credentials/records/{{ holder_cred_record_id }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.protocolState" == "CredentialReceived" + +# Issuer wait for CredentialSent state +GET {{ issuer_url }}/prism-agent/issue-credentials/records/{{ issuer_cred_record_id }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.protocolState" == "CredentialSent" + +############################## +# Presentation Connection +############################## +# Inviter create connection +POST {{ verifier_url }}/prism-agent/connections +{ + "label": "My Connection" +} +HTTP 201 +[Captures] +raw_invitation: jsonpath "$.invitation.invitationUrl" regex ".*_oob=(.*)$" +verifier_connection_id: jsonpath "$.connectionId" + +# Invitee accept connection +POST {{ holder_url }}/prism-agent/connection-invitations +{ + "invitation": "{{ raw_invitation }}" +} +HTTP 200 +[Captures] +holder_connection_id: jsonpath "$.connectionId" + +# Wait for inviter connection status +GET {{ verifier_url }}/prism-agent/connections/{{ verifier_connection_id }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseSent" + +# Wait for invitee connection status +GET {{ holder_url }}/prism-agent/connections/{{ holder_connection_id }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.state" == "ConnectionResponseReceived" + +############################## +# Presentation +############################## +# Verifier create presentation request +POST {{ verifier_url }}/prism-agent/present-proof/presentations +{ + "connectionId": "{{ verifier_connection_id }}", + "proofs":[], + "options": { + "challenge": "11c91493-01b3-4c4d-ac36-b336bab5bddf", + "domain": "https://prism-verifier.com" + } +} +HTTP 201 +[Captures] +verifier_presentation_id: jsonpath "$.presentationId" +didcomm_presentation_thid: jsonpath "$.thid" + +# Holder wait for RequestReceived state +GET {{ holder_url }}/prism-agent/present-proof/presentations +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "RequestReceived" +[Captures] +holder_presentation_id: jsonpath "$.contents[0].presentationId" + +# Holder accept presentation request +PATCH {{ holder_url }}/prism-agent/present-proof/presentations/{{ holder_presentation_id }} +{ + "action": "request-accept", + "proofId": ["{{ holder_cred_record_id }}"] +} +HTTP 200 + +# Holder wait for PresentationSent state +GET {{ holder_url }}/prism-agent/present-proof/presentations +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "PresentationSent" + +# Verfiier wait for PresentationVerified state +GET {{ verifier_url }}/prism-agent/present-proof/presentations +[QueryStringParams] +thid: {{ didcomm_presentation_thid }} +[Options] +retry: -1 +HTTP 200 +[Asserts] +jsonpath "$.contents[0].status" == "PresentationVerified" diff --git a/examples/st-multi/tests/local b/examples/st-multi/tests/local new file mode 100644 index 0000000000..b7f6b17a33 --- /dev/null +++ b/examples/st-multi/tests/local @@ -0,0 +1,3 @@ +holder_url=http://localhost:8081 +issuer_url=http://localhost:8080 +verifier_url=http://localhost:8082 diff --git a/examples/st-oid4vci/README.md b/examples/st-oid4vci/README.md new file mode 100644 index 0000000000..85b357d0a3 --- /dev/null +++ b/examples/st-oid4vci/README.md @@ -0,0 +1,43 @@ +# How to run issuance flow + +## Prerequisites + +- Docker installed v2.24.0 or later +- Python 3 with the following packages installed + - [requests](https://pypi.org/project/requests/) + - [pyjwt](https://pyjwt.readthedocs.io/en/stable/) + - [cryptography](https://cryptography.io/en/latest/) +- Virtual environment (optional) + +Example of the script to install the required packages in a virtual environment: +```shell +python -m venv {path-to-the-project-dir}/identus-cloud-agent/examples/st-oid4vci/python-env +source {path-to-the-project-dir}/identus-cloud-agent/examples/st-oid4vci/python-env/bin/activate +pip install requests pyjwt cryptography +``` + +- the latest Cloud Agent image is built and available in the local Docker registry + +```shell +sbt docker:publishLocal +``` + +### 1. Spin up the agent stack with pre-configured Keycloak + +```bash +docker-compose up +``` + +The Keycloak UI is available at `http://localhost:9980` and the admin username is `admin` with password `admin`. + +### 2. Run the issuance demo script + +```bash +python demo.py +``` + +- 2.1 Follow the instructions in the terminal. The holder will then be asked to log in via a browser +- 2.2 Enter the username `alice` and password `1234` to log in +- 2.3 Grant access for the scopes displayed on the consent UI + +The credential should be issued at the end of the flow and logged to the terminal. diff --git a/examples/st-oid4vci/bootstrap/01_init_realm.hurl b/examples/st-oid4vci/bootstrap/01_init_realm.hurl new file mode 100644 index 0000000000..9caac55ff4 --- /dev/null +++ b/examples/st-oid4vci/bootstrap/01_init_realm.hurl @@ -0,0 +1,90 @@ +# Wait for keycloak ready +GET {{ keycloak_base_url }}/health/ready +[Options] +retry: 300 +HTTP 200 + +# Admin login +POST {{ keycloak_base_url }}/realms/master/protocol/openid-connect/token +[FormParams] +grant_type: password +client_id: admin-cli +username: {{ keycloak_admin_user }} +password: {{ keycloak_admin_password }} +[Options] +retry: 30 +HTTP 200 +[Captures] +admin_access_token: jsonpath "$.access_token" + +# Create realm +POST {{ keycloak_base_url }}/admin/realms +authorization: Bearer {{ admin_access_token }} +{ + "realm": "{{ keycloak_realm }}", + "enabled": true +} +HTTP 201 + +# Create agent client +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/clients +Authorization: Bearer {{ admin_access_token }} +{ + "id": "{{ agent_client_id }}", + "authorizationServicesEnabled": true, + "serviceAccountsEnabled": true, + "secret": "{{ agent_client_secret }}" +} +HTTP 201 + +# Create Alice +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/users +Authorization: Bearer {{ admin_access_token }} +{ + "username": "{{ alice_username }}", + "firstName": "Alice", + "lastName": "Wonderland", + "enabled": true, + "email": "alice@atalaprism.io", + "credentials": [{"value": "{{ alice_password }}", "temporary": false}] +} +HTTP 201 + +############################## +# TODO: actions below to be performed by controller +############################## +# Pre-register holder wallet client // TODO: dynamic registration? +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/clients +Authorization: Bearer {{ admin_access_token }} +{ + "id": "{{ alice_wallet_client_id }}", + "publicClient": true, + "consentRequired": true, + "redirectUris": [ "http://localhost:7777/*" ] +} +HTTP 201 + +# Create a scope for issuable credential +POST {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/client-scopes +Authorization: Bearer {{ admin_access_token }} +{ + "name": "UniversityDegreeCredential", + "description": "The University Degree Credential", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "University Degree", + "display.on.consent.screen": "true", + "include.in.token.scope": "true", + "gui.order": "" + } +} +HTTP 201 +[Captures] +client_scope_id: header "Location" split "/" nth 7 + +# scope mapping +PUT {{ keycloak_base_url }}/admin/realms/{{ keycloak_realm }}/clients/{{ alice_wallet_client_id }}/optional-client-scopes/{{ client_scope_id }} +Authorization: Bearer {{ admin_access_token }} +{} +HTTP 204 + diff --git a/examples/st-oid4vci/compose.yaml b/examples/st-oid4vci/compose.yaml new file mode 100644 index 0000000000..dde0a828ff --- /dev/null +++ b/examples/st-oid4vci/compose.yaml @@ -0,0 +1,151 @@ +configs: + caddyfile_issuer: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-issuer:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-issuer:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-issuer:8080 + } + handle_path /vault* { + reverse_proxy vault-issuer:8200 + } + } +services: + agent-issuer: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-issuer + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-issuer + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-issuer:8080/didcomm + POLLUX_DB_HOST: db-issuer + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-issuer:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-issuer:8080/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-issuer: + configs: + - source: caddyfile_issuer + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + db-issuer: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_issuer:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + external-keycloak-init-issuer: + command: + - --glob + - /hurl/*.hurl + - --test + environment: + HURL_agent_client_id: cloud-agent + HURL_agent_client_secret: secret + HURL_alice_password: '1234' + HURL_alice_username: alice + HURL_alice_wallet_client_id: alice-wallet + HURL_keycloak_admin_password: admin + HURL_keycloak_admin_user: admin + HURL_keycloak_base_url: http://external-keycloak-issuer:8080 + HURL_keycloak_realm: students + image: ghcr.io/orange-opensource/hurl:4.2.0 + volumes: + - ./bootstrap:/hurl + external-keycloak-issuer: + command: + - start-dev + - --features=preview + - --health-enabled=true + - --hostname-url=http://localhost:9980 + - --hostname-admin-url=http://localhost:9980 + environment: + IDENTUS_URL: http://caddy-issuer:8080/prism-agent + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + image: ghcr.io/hyperledger/identus-keycloak-plugins:0.1.0 + ports: + - 9980:8080 + restart: always + mockserver: + image: mockserver/mockserver:5.15.0 + ports: + - 7777:1080 + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql +volumes: + pg_data_issuer: {} + pg_data_node: {} diff --git a/examples/st-oid4vci/demo.py b/examples/st-oid4vci/demo.py new file mode 100755 index 0000000000..0a1b97121e --- /dev/null +++ b/examples/st-oid4vci/demo.py @@ -0,0 +1,314 @@ +import json +import jwt +import requests +import time +import urllib + +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives import serialization + + +MOCKSERVER_URL = "http://localhost:7777" +LOGIN_REDIRECT_URL = "http://localhost:7777/cb" + +AGENT_URL = "http://localhost:8080/prism-agent" +CREDENTIAL_ISSUER = None +CREDENTIAL_ISSUER_DID = None +CREDENTIAL_CONFIGURATION_ID = "UniversityDegreeCredential" +AUTHORIZATION_SERVER = "http://localhost:9980/realms/students" + +ALICE_CLIENT_ID = "alice-wallet" + +HOLDER_LONG_FORM_DID = "did:prism:73196107e806b084d44339c847a3ae8dd279562f23895583f62cc91a2ee5b8fe:CnsKeRI8CghtYXN0ZXItMBABSi4KCXNlY3AyNTZrMRIhArrplJNfQYxthryRU87XdODy-YWUh5mqrvIfAdoZFeJBEjkKBWtleS0wEAJKLgoJc2VjcDI1NmsxEiEC8rsFplfYvRLazdWWi3LNR1gaAQXb-adVhZacJT4ntwE" +HOLDER_ASSERTION_PRIVATE_KEY_HEX = ( + "2902637d412190fb08f5d0e0b2efc1eefae8060ae151e7951b69afbecbdd452e" +) + + +def prepare_mock_server(): + # reset mock server + requests.put(f"{MOCKSERVER_URL}/mockserver/reset") + + # mock wallet authorization callback endpoint + requests.put( + f"{MOCKSERVER_URL}/mockserver/expectation", + json={ + "httpRequest": {"path": "/cb"}, + "httpResponse": { + "statusCode": 200, + "body": {"type": "string", "string": "Login Successful"}, + }, + }, + ) + + +def prepare_issuer(): + # prepare issuging DID + dids = requests.get(f"{AGENT_URL}/did-registrar/dids").json()["contents"] + if len(dids) == 0: + requests.post( + f"{AGENT_URL}/did-registrar/dids", + json={ + "documentTemplate": { + "publicKeys": [{"id": "iss", "purpose": "assertionMethod"}], + "services": [], + } + }, + ) + dids = requests.get(f"{AGENT_URL}/did-registrar/dids").json()["contents"] + + issuer_did = dids[0] + while issuer_did["status"] != "PUBLISHED": + time.sleep(2) + canonical_did = issuer_did["did"] + issuer_did = requests.get( + f"{AGENT_URL}/did-registrar/dids/{canonical_did}" + ).json() + + # publish if not pending + if issuer_did["status"] == "CREATED": + requests.post( + f"{AGENT_URL}/did-registrar/dids/{canonical_did}/publications" + ) + canonical_did = issuer_did["did"] + global CREDENTIAL_ISSUER_DID + CREDENTIAL_ISSUER_DID = canonical_did + + # prepare schema + schema = requests.post( + f"{AGENT_URL}/schema-registry/schemas", + json={ + "name": "UniversityDegree", + "version": "1.0.0", + "type": "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json", + "schema": { + "$id": "https://example.com/driving-license-1.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "firstName": {"type": "string"}, + "degree": {"type": "string"}, + "grade": {"type": "number"}, + }, + "required": ["firstName", "grade"], + "additionalProperties": False, + }, + "tags": [], + "author": canonical_did, + }, + ).json() + schema_guid = schema["guid"] + + # prepare issuer + credential_issuer = requests.post( + f"{AGENT_URL}/oid4vci/issuers", + json={ + "authorizationServer": { + "url": "http://external-keycloak-issuer:8080/realms/students", + "clientId": "cloud-agent", + "clientSecret": "secret", + } + }, + ).json() + issuer_id = credential_issuer["id"] + global CREDENTIAL_ISSUER + CREDENTIAL_ISSUER = f"{AGENT_URL}/oid4vci/issuers/{issuer_id}" + + # prepare credential configuration + cred_config = requests.post( + f"{CREDENTIAL_ISSUER}/credential-configurations", + json={ + "configurationId": CREDENTIAL_CONFIGURATION_ID, + "format": "jwt_vc_json", + # TODO: align docker host URL + "schemaId": f"http://localhost:8085/schema-registry/schemas/{schema_guid}/schema", + }, + ).json() + + +def issuer_create_credential_offer(claims): + response = requests.post( + f"{CREDENTIAL_ISSUER}/credential-offers", + json={ + "credentialConfigurationId": CREDENTIAL_CONFIGURATION_ID, + "issuingDID": CREDENTIAL_ISSUER_DID, + "claims": claims, + }, + ) + return response.json()["credentialOffer"] + + +def holder_get_issuer_metadata(credential_issuer: str): + # FIXME: override this just to make url reachable from host + def override_host(url): + return url.replace("http://caddy-issuer:8080/prism-agent", AGENT_URL) + + metadata_url = override_host( + f"{credential_issuer}/.well-known/openid-credential-issuer" + ) + response = requests.get(metadata_url).json() + response["credential_endpoint"] = override_host(response["credential_endpoint"]) + response["credential_issuer"] = override_host(response["credential_issuer"]) + response["authorization_servers"] = [ + AUTHORIZATION_SERVER for s in response["authorization_servers"] + ] + return response + + +def holder_get_issuer_as_metadata(authorization_server: str): + metadata_url = f"{authorization_server}/.well-known/openid-configuration" + response = requests.get(metadata_url) + metadata = response.json() + print(f"Metadata: {metadata}") + return metadata + + +def holder_start_login_flow(auth_endpoint: str, token_endpoint: str, issuer_state: str): + def wait_redirect_authorization_code() -> str: + print("wating for authorization redirect ...") + while True: + response = requests.put( + f"{MOCKSERVER_URL}/mockserver/retrieve?type=REQUESTS", + json={"path": "/cb", "method": "GET"}, + ).json() + if len(response) > 0: + break + time.sleep(1) + + authorzation_code = response[0]["queryStringParameters"]["code"][0] + print(f"code: {authorzation_code}") + return authorzation_code + + def start_authorization_request(auth_endpoint: str, issuer_state: str): + # Authorization Request + queries = urllib.parse.urlencode( + { + "redirect_uri": LOGIN_REDIRECT_URL, + "response_type": "code", + "client_id": ALICE_CLIENT_ID, + "scope": "openid " + CREDENTIAL_CONFIGURATION_ID, + "issuer_state": issuer_state, + } + ) + login_url = f"{auth_endpoint}?{queries}" + print("\n##############################\n") + print("Open this link in the browser to login\n") + print(login_url) + print("\n##############################\n") + + # wait for authorization redirect + authorzation_code = wait_redirect_authorization_code() + return authorzation_code + + def start_token_request(token_endpoint: str, authorization_code: str): + # Token Request + response = requests.post( + token_endpoint, + data={ + "grant_type": "authorization_code", + "code": authorization_code, + "client_id": ALICE_CLIENT_ID, + "redirect_uri": LOGIN_REDIRECT_URL, + }, + ) + return response.json() + + authorization_code = start_authorization_request(auth_endpoint, issuer_state) + token_response = start_token_request(token_endpoint, authorization_code) + return token_response + + +def holder_extract_credential_offer(offer_uri: str): + queries = urllib.parse.urlparse(credential_offer_uri).query + credential_offer = urllib.parse.parse_qs(queries)["credential_offer"] + return json.loads(credential_offer[0]) + + +def holder_get_credential(credential_endpoint: str, token_response): + access_token = token_response["access_token"] + c_nonce = token_response["c_nonce"] + + # generate proof + private_key_bytes = bytes.fromhex(HOLDER_ASSERTION_PRIVATE_KEY_HEX) + private_key = ec.derive_private_key( + int.from_bytes(private_key_bytes, "big"), ec.SECP256K1() + ) + public_key = private_key.public_key() + jwt_proof = jwt.encode( + headers={ + "typ": "openid4vci-proof+jwt", + "kid": HOLDER_LONG_FORM_DID, + }, + payload={ + "iss": ALICE_CLIENT_ID, + "aud": CREDENTIAL_ISSUER, + "iat": int(time.time()), + "nonce": c_nonce, + }, + key=private_key, + algorithm="ES256K", # TODO: switch to EdDSA alg (Ed25519) + ) + + response = requests.post( + credential_endpoint, + headers={"Authorization": f"Bearer {access_token}"}, + json={ + "format": "jwt_vc_json", + "credential_definition": { + "type": ["VerifiableCredential", CREDENTIAL_CONFIGURATION_ID], + "credentialSubject": {}, + }, + "proof": {"proof_type": "jwt", "jwt": jwt_proof}, + }, + ) + return response.json() + + +if __name__ == "__main__": + prepare_mock_server() + prepare_issuer() + + # step 1: Issuer create CredentialOffer + credential_offer_uri = issuer_create_credential_offer( + {"firstName": "Alice", "degree": "ChemicalEngineering", "grade": 3.2} + ) + + # step 2: Issuer present QR code container CredentialOffer URI + credential_offer = holder_extract_credential_offer(credential_offer_uri) + credential_offer_pretty = json.dumps(credential_offer, indent=2) + issuer_state = credential_offer["grants"]["authorization_code"]["issuer_state"] + print("\n##############################\n") + print(f"QR code scanned, got credential-offer\n\n{credential_offer_uri}\n") + print(f"\n{credential_offer_pretty}\n") + print("\n##############################\n") + input("\nEnter to continue ...") + + # step 3: Holdler retreive Issuer's metadata + issuer_metadata = holder_get_issuer_metadata(CREDENTIAL_ISSUER) + authorzation_server = issuer_metadata["authorization_servers"][0] + print("\n::::: Issuer Metadata :::::") + print(json.dumps(issuer_metadata, indent=2)) + input("\nEnter to continue ...") + + # step 3.1: Holder retreive Issuer's AS metadata + issuer_as_metadata = holder_get_issuer_as_metadata(authorzation_server) + issuer_as_token_endpoint = issuer_as_metadata["token_endpoint"] + issuer_as_authorization_endpoint = issuer_as_metadata["authorization_endpoint"] + print("\n::::: Issuer Authorization Server Metadata :::::") + print(f"issuer_as_auth_endpoint: {issuer_as_authorization_endpoint}") + print(f"issuer_as_token_endpoint: {issuer_as_token_endpoint}") + input("\nEnter to continue ...") + + # step 4: Holder start authorization flow + token_response = holder_start_login_flow( + issuer_as_authorization_endpoint, issuer_as_token_endpoint, issuer_state + ) + print("::::: Token Response :::::") + print(json.dumps(token_response, indent=2)) + input("\nEnter to continue ...") + + # step 5: Holder use access_token to get credential + credential_endpoint = issuer_metadata["credential_endpoint"] + jwt_credential = holder_get_credential(credential_endpoint, token_response) + print("\n::::: Credential Received :::::") + print(json.dumps(jwt_credential, indent=2)) diff --git a/examples/st-vault/compose.yaml b/examples/st-vault/compose.yaml new file mode 100644 index 0000000000..911a4a1f42 --- /dev/null +++ b/examples/st-vault/compose.yaml @@ -0,0 +1,133 @@ +configs: + caddyfile_issuer: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-issuer:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-issuer:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-issuer:8080 + } + handle_path /vault* { + reverse_proxy vault-issuer:8200 + } + } +services: + agent-issuer: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-issuer + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-issuer + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-issuer:8080/didcomm + POLLUX_DB_HOST: db-issuer + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-issuer:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-issuer:8080/prism-agent + SECRET_STORAGE_BACKEND: vault + VAULT_ADDR: http://vault-issuer:8200 + VAULT_TOKEN: admin + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-issuer: + configs: + - source: caddyfile_issuer + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + db-issuer: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_issuer:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + vault-issuer: + cap_add: + - IPC_LOCK + environment: + VAULT_ADDR: http://0.0.0.0:8200 + VAULT_DEV_ROOT_TOKEN_ID: admin + healthcheck: + interval: 10s + retries: '5' + test: + - CMD + - vault + - status + timeout: 5s + image: hashicorp/vault:1.15.6 + ports: + - 8200:8200 +volumes: + pg_data_issuer: {} + pg_data_node: {} diff --git a/examples/st/compose.yaml b/examples/st/compose.yaml new file mode 100644 index 0000000000..167a2a199e --- /dev/null +++ b/examples/st/compose.yaml @@ -0,0 +1,114 @@ +configs: + caddyfile_issuer: + content: |- + :8080 { + handle_path /didcomm* { + reverse_proxy agent-issuer:8090 + } + handle_path /prism-agent* { + reverse_proxy agent-issuer:8085 + } + handle_path /keycloak* { + reverse_proxy keycloak-issuer:8080 + } + handle_path /vault* { + reverse_proxy vault-issuer:8200 + } + } +services: + agent-issuer: + depends_on: + node: + condition: service_started + environment: + ADMIN_TOKEN: admin + AGENT_DB_HOST: db-issuer + AGENT_DB_NAME: agent + AGENT_DB_PASSWORD: postgres + AGENT_DB_PORT: '5432' + AGENT_DB_USER: postgres + API_KEY_ENABLED: 'false' + CONNECT_DB_HOST: db-issuer + CONNECT_DB_NAME: connect + CONNECT_DB_PASSWORD: postgres + CONNECT_DB_PORT: '5432' + CONNECT_DB_USER: postgres + DIDCOMM_SERVICE_URL: http://caddy-issuer:8080/didcomm + POLLUX_DB_HOST: db-issuer + POLLUX_DB_NAME: pollux + POLLUX_DB_PASSWORD: postgres + POLLUX_DB_PORT: '5432' + POLLUX_DB_USER: postgres + POLLUX_STATUS_LIST_REGISTRY_PUBLIC_URL: http://caddy-issuer:8080/prism-agent + PRISM_NODE_HOST: node + PRISM_NODE_PORT: '50053' + REST_SERVICE_URL: http://caddy-issuer:8080/prism-agent + SECRET_STORAGE_BACKEND: postgres + image: ghcr.io/hyperledger/identus-cloud-agent:1.36.1-SNAPSHOT + restart: always + caddy-issuer: + configs: + - source: caddyfile_issuer + target: /etc/caddy/Caddyfile + image: caddy:2.7.6-alpine + ports: + - 8080:8080 + restart: always + db-issuer: + environment: + POSTGRES_MULTIPLE_DATABASES: pollux,connect,agent + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_issuer:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql + node: + depends_on: + node-db: + condition: service_healthy + environment: + NODE_PSQL_DATABASE: node_db + NODE_PSQL_HOST: node-db:5432 + NODE_PSQL_PASSWORD: postgres + NODE_PSQL_USERNAME: postgres + image: ghcr.io/input-output-hk/prism-node:2.4.0 + restart: always + node-db: + environment: + POSTGRES_MULTIPLE_DATABASES: node_db + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + interval: 10s + retries: 5 + test: + - CMD + - pg_isready + - -U + - postgres + - -d + - postgres + timeout: 5s + image: postgres:13 + restart: always + volumes: + - pg_data_node:/var/lib/postgresql/data + - ../.shared/postgres/init-script.sh:/docker-entrypoint-initdb.d/init-script.sh + - ../.shared/postgres/max_conns.sql:/docker-entrypoint-initdb.d/max_conns.sql +volumes: + pg_data_issuer: {} + pg_data_node: {} diff --git a/examples/trinsic-credentials-v1-resolved.json b/examples/trinsic-credentials-v1-resolved.json deleted file mode 100644 index 643e777a51..0000000000 --- a/examples/trinsic-credentials-v1-resolved.json +++ /dev/null @@ -1,3039 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "description": "An API to issue, manage, and verify digital credentials", - "version": "v1", - "title": "Credentials API", - "termsOfService": "https://trinsic.id/terms/", - "contact": { - "name": "Trinsic Engineering Team", - "url": "https://trinsic.id/contact-us", - "email": "support@trinsic.id" - } - }, - "host": "api.trinsic.id", - "basePath": "/credentials/v1", - "schemes": [ - "https" - ], - "security": [ - { - "oauth2": [] - } - ], - "paths": { - "/common/upload": { - "post": { - "tags": [ - "Common" - ], - "summary": "[Deprecated] Upload image", - "description": "Please use the Provider API instead.\r\nUpload an image and return a URL with the static remote location.", - "operationId": "UploadImage", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "uploadedFiles", - "in": "formData", - "required": true, - "type": "file" - }, - { - "name": "filename", - "in": "formData", - "required": false, - "type": "string" - }, - { - "name": "contentType", - "in": "formData", - "required": false, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "string", - "format": "uri" - } - } - } - } - }, - "/common/networks": { - "get": { - "tags": [ - "Common" - ], - "summary": "[Deprecated] List all ledger networks", - "description": "Please use the Provider API instead.\r\nList all available ledger networks.\r\nSome networks are not available based on your subscription.", - "operationId": "ListNetworks", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/NetworkContract" - } - } - } - } - } - }, - "/common/networks/{networkId}/txnAuthorAgreement": { - "get": { - "tags": [ - "Common" - ], - "summary": "[Deprecated] Get network transaction author agreement", - "description": "This endpoint is no longer needed.\r\nGet the latest transaction author agreement and acceptance methods if one is set on the specified network.", - "operationId": "GetTransactionAuthorAgreement", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "networkId", - "in": "path", - "description": "The network identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/NetworkTxnAgreementContract" - } - } - } - } - }, - "/common/networks/{tenantId}/txnAuthorAgreement": { - "put": { - "tags": [ - "Common" - ], - "summary": "[Deprecated] Accept network transaction author agreement", - "description": "This endpoint is no longer needed.\r\nAccept the latest transaction author agreement on the specified network.", - "operationId": "AcceptTransactionAuthorAgreement", - "parameters": [ - { - "name": "tenantId", - "in": "path", - "description": "The tenant identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/connections": { - "get": { - "tags": [ - "Connections" - ], - "summary": "List all connections", - "description": "Retrieve a list of all connections.\r\nOptionally, list only connections in a specified state.", - "operationId": "ListConnections", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "state", - "in": "query", - "description": "The connection state", - "required": false, - "type": "string", - "enum": [ - "Invited", - "Negotiating", - "Connected" - ] - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/ConnectionContract" - } - } - } - } - }, - "post": { - "tags": [ - "Connections" - ], - "summary": "Create connection", - "description": "Initiate a new connection by creating an invitation.\r\nThe newly created connection record will be in state 'Invited' until the other party has accepted the invitation. \r\nThe response body includes details about the newly creation connection.", - "operationId": "CreateConnection", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "Connection invitation parameters", - "required": true, - "schema": { - "$ref": "#/definitions/ConnectionInvitationParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/ConnectionContract" - } - } - } - } - }, - "/connections/{connectionId}": { - "get": { - "tags": [ - "Connections" - ], - "summary": "Get connection", - "description": "Get the specified connection.", - "operationId": "GetConnection", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "path", - "description": "The connection identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/ConnectionContract" - } - } - } - }, - "delete": { - "tags": [ - "Connections" - ], - "summary": "Delete connection", - "description": "Delete the specified connection.", - "operationId": "DeleteConnection", - "parameters": [ - { - "name": "connectionId", - "in": "path", - "description": "The connection identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/credentials": { - "get": { - "tags": [ - "Credentials" - ], - "summary": "List all credentials", - "description": "List all credentials that match any specified query parameters.\r\nNo query parameters are required, but any provided will filter the results.", - "operationId": "ListCredentials", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "query", - "description": "A connection identifier", - "required": false, - "type": "string" - }, - { - "name": "state", - "in": "query", - "description": "The state of credentials", - "required": false, - "type": "string", - "enum": [ - "Offered", - "Requested", - "Issued", - "Rejected", - "Revoked" - ] - }, - { - "name": "definitionId", - "in": "query", - "description": "A credential definition identifier", - "required": false, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/CredentialContract" - } - } - } - } - }, - "post": { - "tags": [ - "Credentials" - ], - "summary": "Create and offer credential", - "description": "Send a credential offer of the specified credential definition to the specified connection.", - "operationId": "CreateCredential", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The definition and connection to which this offer will be sent", - "required": true, - "schema": { - "$ref": "#/definitions/CredentialOfferParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/CredentialContract" - } - } - } - } - }, - "/credentials/{credentialId}": { - "get": { - "tags": [ - "Credentials" - ], - "summary": "Get credential", - "description": "Get the specified credential.", - "operationId": "GetCredential", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "credentialId", - "in": "path", - "description": "The credential identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/CredentialContract" - } - } - } - }, - "put": { - "tags": [ - "Credentials" - ], - "summary": "Issue credential", - "description": "Issue the specified credential.\r\nIf the values offered were incorrect, changes to the values may be made here.\r\nYou must update all of the values, and they must be follow the same structure of the schema.\r\nTo keep the values the same as those included in the credential offer, leave the body blank.", - "operationId": "IssueCredential", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "parameters": [ - { - "name": "credentialId", - "in": "path", - "description": "The credential identifier", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "body", - "description": "Updated credential values", - "required": false, - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - }, - "delete": { - "tags": [ - "Credentials" - ], - "summary": "Revoke an issued credential", - "description": "Revoke credential that was issued previously.\r\nProcess of revocation will update the revocation registry locally and on the ledger.\r\nIssued credentials can still participate in proof workflows and be considered valid, but only if the verifying ignores the revocation trail.", - "operationId": "RevokeCredential", - "parameters": [ - { - "name": "credentialId", - "in": "path", - "description": "The credential identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/credentials/delete/{credentialId}": { - "delete": { - "tags": [ - "Credentials" - ], - "summary": "Delete credential", - "description": "Delete the specified credential.\r\nThis endpoint does not revoke the credential.", - "operationId": "DeleteCredential", - "parameters": [ - { - "name": "credentialId", - "in": "path", - "description": "The credential identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/verificationPolicies": { - "get": { - "tags": [ - "Verification Policies" - ], - "summary": "List all verification policies", - "description": "List all verification policies for the authenticated organization.", - "operationId": "ListVerificationPolicies", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyContract" - } - } - } - } - }, - "post": { - "tags": [ - "Verification Policies" - ], - "summary": "Create verification policy", - "description": "Create a verification policy from the specified parameters.", - "operationId": "CreateVerificationPolicy", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The verification policy parameters", - "required": true, - "schema": { - "$ref": "#/definitions/VerificationPolicyParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationPolicyContract" - } - } - } - } - }, - "/verificationPolicies/{policyId}": { - "get": { - "tags": [ - "Verification Policies" - ], - "summary": "Get verification policy", - "description": "Get the specified verification policy.", - "operationId": "GetVerificationPolicy", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "policyId", - "in": "path", - "description": "The verification policy identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationPolicyContract" - } - } - } - }, - "delete": { - "tags": [ - "Verification Policies" - ], - "summary": "Delete verification policy", - "description": "Delete the specified verification policy.", - "operationId": "DeleteVerificationPolicy", - "parameters": [ - { - "name": "policyId", - "in": "path", - "description": "The verification policy identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/definitions/credentials": { - "get": { - "tags": [ - "Definitions" - ], - "summary": "List all credential definitions", - "description": "List all credential definitions for the authorization context.", - "operationId": "ListCredentialDefinitions", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/CredentialDefinitionContract" - } - } - } - } - }, - "post": { - "tags": [ - "Definitions" - ], - "summary": "Create credential definition and schema from parameters", - "description": "A credential definition is created and saved on your cloud agent.", - "operationId": "CreateCredentialDefinition", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "Definition parameters", - "required": true, - "schema": { - "$ref": "#/definitions/CredentialDefinitionFromSchemaParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/CredentialDefinitionContract" - } - } - } - } - }, - "/definitions/credentials/{definitionId}": { - "get": { - "tags": [ - "Definitions" - ], - "summary": "Get credential definition", - "description": "Get the specified credential definition.", - "operationId": "GetCredentialDefinition", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "definitionId", - "in": "path", - "description": "The credential definition identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/CredentialDefinitionContract" - } - } - } - } - }, - "/definitions/credentials/{schemaId}": { - "post": { - "tags": [ - "Definitions" - ], - "summary": "Create credential definition from schema", - "description": "Create a credential definition from the specified schema.", - "operationId": "CreateCredentialDefinitionForSchemaId", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "schemaId", - "in": "path", - "description": "The schema identifier", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "body", - "description": "The definition parameters", - "required": true, - "schema": { - "$ref": "#/definitions/CredentialDefinitionParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/CredentialDefinitionContract" - } - } - } - } - }, - "/definitions/credentials/{credentialDefinitionId}": { - "delete": { - "tags": [ - "Definitions" - ], - "summary": "Delete credential definition", - "description": "Delete the specified credential definition.", - "operationId": "DeleteCredentialDefinition", - "parameters": [ - { - "name": "credentialDefinitionId", - "in": "path", - "description": "The credential definition identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/definitions/schemas": { - "get": { - "tags": [ - "Definitions" - ], - "summary": "List all schemas", - "description": "List all schemas registered to or used by the authenticated organization.", - "operationId": "ListSchemas", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/SchemaRecord" - } - } - } - } - }, - "post": { - "tags": [ - "Definitions" - ], - "summary": "Create schema", - "description": "Register schema with the current agency tenant and write the schema to the ledger using the tenant as issuer.\r\nThis does not create credential definition.", - "operationId": "CreateSchema", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "Schema parameters", - "required": true, - "schema": { - "$ref": "#/definitions/SchemaParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "string" - } - } - } - } - }, - "/definitions/verifications/{definitionId}": { - "get": { - "tags": [ - "Definitions" - ], - "summary": "[Deprecated] Get verification definition", - "description": "Please use Verification Policies endpoints.\r\nGet the specified verification definition.", - "operationId": "GetVerificationDefinition", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "definitionId", - "in": "path", - "description": "The verification definition identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationDefinitionContract" - } - } - } - } - }, - "/definitions/verifications": { - "get": { - "tags": [ - "Definitions" - ], - "summary": "[Deprecated] List all verification definitions", - "description": "Please use Verification Policies endpoints.\r\nList all verification definitions for the authenticated organization.", - "operationId": "ListVerificationDefinitions", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationDefinitionContract" - } - } - } - } - }, - "post": { - "tags": [ - "Definitions" - ], - "summary": "[Deprecated] Create verification definition", - "description": "Please use Verification Policies endpoints.\r\nA verification definition is created and saved on your cloud agent.\r\nYou can discover your definition based on the ID that is returned or the name.", - "operationId": "CreateVerificationDefinition", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The proof request", - "required": true, - "schema": { - "$ref": "#/definitions/ProofRequest" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationDefinitionContract" - } - } - } - } - }, - "/definitions/verifications/{verificationDefinitionId}": { - "delete": { - "tags": [ - "Definitions" - ], - "summary": "[Deprecated] Delete verification definition", - "description": "Please use Verification Policies endpoints.\r\nDelete the specified verification definition.", - "operationId": "DeleteVerificationDefinition", - "parameters": [ - { - "name": "verificationDefinitionId", - "in": "path", - "description": "The verification definition identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/messages": { - "post": { - "tags": [ - "Messaging" - ], - "summary": "Send message", - "description": "Send a message from the specified message parameters.", - "operationId": "SendMessage", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The message parameters", - "required": true, - "schema": { - "$ref": "#/definitions/BasicMessageParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/messages/connection/{connectionId}": { - "get": { - "tags": [ - "Messaging" - ], - "summary": "List all messages for connection", - "description": "List all messages for the specified connection.", - "operationId": "ListMessages", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "path", - "description": "The connection identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/BasicMessageRecord" - } - } - } - } - } - }, - "/messages/{messageId}": { - "get": { - "tags": [ - "Messaging" - ], - "summary": "Get message", - "description": "Get the details for the specified message.", - "operationId": "GetMessage", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "messageId", - "in": "path", - "description": "The message identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/BasicMessageContract" - } - } - } - } - }, - "/tenants": { - "get": { - "tags": [ - "Tenants" - ], - "summary": "[Deprecated] List all tenants", - "description": "Please use the Provider API instead.\r\nList all tenants for the current authorization context.", - "operationId": "ListTenants", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/TenantContract" - } - } - } - } - }, - "post": { - "tags": [ - "Tenants" - ], - "summary": "[Deprecated] Create tenant", - "description": "Please use the Provider API instead.\r\nCreate a new tenant and setup a unique agency endpoint.\r\nThe agency will be set as an issuer.", - "operationId": "CreateTenant", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "Configuration options for creating new tenant", - "required": true, - "schema": { - "$ref": "#/definitions/TenantParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/TenantContract" - } - } - } - } - }, - "/tenants/{tenantId}": { - "get": { - "tags": [ - "Tenants" - ], - "summary": "[Deprectaed] Get tenant", - "description": "Please use the Provider API instead.\r\nGet the configuration for the specified tenant.", - "operationId": "GetTenant", - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": "tenantId", - "in": "path", - "description": "The tenant identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/TenantContract" - } - } - } - }, - "delete": { - "tags": [ - "Tenants" - ], - "summary": "[Deprecated] Delete tenant", - "description": "Please use the Provider API instead.\r\nPermanently remove the specified tenant, including their wallet, endpoint registrations and all data.\r\nAll definitions, connections and credentials issued will be deleted.\r\nThis action cannot be reverted.", - "operationId": "DeleteTenant", - "parameters": [ - { - "name": "tenantId", - "in": "path", - "description": "The tenant identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/tenants/issuerStatus": { - "get": { - "tags": [ - "Tenants" - ], - "summary": "[Deprecated] Get issuer status for current tenant", - "description": "Please use the Provider API instead.\r\nIf the tenant is configured with Dedicated endorsement, this action will check if the issuer DID has the required ENDORSER role on the configured ledger network.\r\nAdditionally, check the acceptance of the transaction author agreement and return the text and version if acceptance is required.", - "operationId": "GetIssuerStatus", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/IssuerStatusContract" - } - } - } - } - }, - "/verifications/policy/{policyId}/connections/{connectionId}": { - "put": { - "tags": [ - "Verifications" - ], - "summary": "Send verification to connection from policy", - "description": "Send a verification to the specified connection using an existing policy.", - "operationId": "SendVerificationFromPolicy", - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "path", - "description": "The connection identifier", - "required": true, - "type": "string" - }, - { - "name": "policyId", - "in": "path", - "description": "The policy identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "/verifications/policy/connections/{connectionId}": { - "post": { - "tags": [ - "Verifications" - ], - "summary": "Send verification to connection from parameters", - "description": "Send a verification request to the specified connection from a set of parameters.", - "operationId": "SendVerificationFromParameters", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "path", - "description": "The connection identifier", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "body", - "description": "The policy parameters", - "required": true, - "schema": { - "$ref": "#/definitions/VerificationPolicyParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "/verifications/proposal/{policyId}/connections/{connectionId}": { - "put": { - "tags": [ - "Verifications" - ], - "summary": "Send verification from policy", - "description": "Create a verification from the specified policy and send it to the specified connection.", - "operationId": "SendVerificationFromProposal", - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "path", - "description": "The connection identifier", - "required": true, - "type": "string" - }, - { - "name": "policyId", - "in": "path", - "description": "The verification policy identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "/verifications/policy/{policyId}": { - "put": { - "tags": [ - "Verifications" - ], - "summary": "Create connectionless verification from policy", - "description": "Create a connectionless verification from the specified policy.\r\nConnectionless transport uses URLs that can be shared with the user over any existing transport (email, SMS, web).", - "operationId": "CreateVerificationFromPolicy", - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "policyId", - "in": "path", - "description": "The policy identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "/verifications/policy": { - "post": { - "tags": [ - "Verifications" - ], - "summary": "Create connectionless verification from parameters", - "description": "Create verification from parameters.\r\nConnectionless transport uses URLs that can be shared with the user over any existing transport (email, SMS, web).", - "operationId": "CreateVerificationFromParameters", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The policy parameters", - "required": true, - "schema": { - "$ref": "#/definitions/VerificationPolicyParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "/verifications": { - "get": { - "tags": [ - "Verifications" - ], - "summary": "List all verifications", - "description": "List all verifications.\r\nOptionally filter by connection and/or definition.", - "operationId": "ListVerifications", - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "connectionId", - "in": "query", - "description": "The connection identifier", - "required": false, - "type": "string" - }, - { - "name": "definitionId", - "in": "query", - "description": "The definition identifier", - "required": false, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "post": { - "tags": [ - "Verifications" - ], - "summary": "[Deprecated] Create/send verification", - "description": "Please use SendVerification(policyId) or CreateVerification(policyId) instead.\r\nThis endpoint can be used to send a verification definition to a connection, which will create a verification ID to track the response from the connection.\r\nIf the parameter {connectionId} is not specified, this endpoint will create a connectionless verification. A URL will be generated that can be shared with anonymous user.", - "operationId": "CreateVerification", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The verification parameters", - "required": true, - "schema": { - "$ref": "#/definitions/VerificationParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - } - }, - "/verifications/{verificationId}": { - "get": { - "tags": [ - "Verifications" - ], - "summary": "Get verification", - "description": "Get the specified verification.", - "operationId": "GetVerification", - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "verificationId", - "in": "path", - "description": "The verification identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationContract" - } - } - } - }, - "delete": { - "tags": [ - "Verifications" - ], - "summary": "Delete verification", - "description": "Delete the specified verification.", - "operationId": "DeleteVerification", - "parameters": [ - { - "name": "verificationId", - "in": "path", - "description": "The verification identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/verifications/{verificationId}/verify": { - "get": { - "tags": [ - "Verifications" - ], - "summary": "[Deprecated] Verify verification", - "description": "This action is now obsolete. Verifications are automatically verified when they are received.\r\nExecute verification on this record. This is an expensive action and is executed by verifying the proof against the ledger data.", - "operationId": "VerifyVerification", - "produces": [ - "text/plain", - "application/json", - "text/json" - ], - "parameters": [ - { - "name": "verificationId", - "in": "path", - "description": "The verification identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "deprecated": true, - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/VerificationResult" - } - } - } - } - }, - "/webhooks": { - "get": { - "tags": [ - "Webhooks" - ], - "summary": "List all webhooks", - "description": "List all webhooks registered with the authenticated organization.", - "operationId": "ListWebhooks", - "produces": [ - "application/json" - ], - "parameters": [], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/WebhookContract" - } - } - } - } - }, - "post": { - "tags": [ - "Webhooks" - ], - "summary": "Create webhook", - "description": "Register a webhook with the authenticated organization.", - "operationId": "CreateWebhook", - "consumes": [ - "application/json", - "text/json", - "application/*+json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The webhook parameters", - "required": true, - "schema": { - "$ref": "#/definitions/WebhookParameters" - } - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/WebhookContract" - } - } - } - } - }, - "/webhooks/{webhookId}": { - "delete": { - "tags": [ - "Webhooks" - ], - "summary": "Remove webhook", - "description": "Remove a registered webhook from the authenticated organization.", - "operationId": "RemoveWebhook", - "parameters": [ - { - "name": "webhookId", - "in": "path", - "description": "The webhook identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/webhooks/{webhookId}/enable": { - "put": { - "tags": [ - "Webhooks" - ], - "summary": "Enable webhook", - "description": "Enable a registered webhook for the authenticated organization.", - "operationId": "EnableWebhook", - "parameters": [ - { - "name": "webhookId", - "in": "path", - "description": "The webhook identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/webhooks/{webhookId}/disable": { - "put": { - "tags": [ - "Webhooks" - ], - "summary": "Disable webhook", - "description": "Enable a registered webhook for the authenticated organization.", - "operationId": "DisableWebhook", - "parameters": [ - { - "name": "webhookId", - "in": "path", - "description": "The webhook identifier", - "required": true, - "type": "string" - } - ], - "security": [ - { - "oauth2": [] - } - ], - "responses": { - "200": { - "description": "Success" - } - } - } - }, - "/health": { - "get": { - "tags": [ - "Diagnostics" - ], - "summary": "Health check", - "description": "Check the health of the API.", - "operationId": "Health", - "parameters": [], - "responses": { - "200": { - "description": "Success" - } - } - } - } - }, - "securityDefinitions": { - "oauth2": { - "description": "Standard Authorization header using the Bearer scheme and an Access Token. Example: \"Bearer {Access Token}\"", - "type": "apiKey", - "name": "Authorization", - "in": "header" - } - }, - "definitions": { - "NetworkContract": { - "type": "object", - "properties": { - "networkId": { - "type": "string" - }, - "networkName": { - "type": "string" - }, - "genesisTransactions": { - "type": "string" - }, - "poolProtocolVersion": { - "type": "integer", - "format": "int32" - } - } - }, - "NetworkTxnAgreementContract": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Acceptance agreement text" - }, - "version": { - "type": "string", - "description": "Agreement version" - }, - "acceptanceMethods": { - "type": "object", - "description": "List of agreement acceptance methods", - "additionalProperties": { - "type": "string" - } - } - }, - "description": "Transaction author agreement" - }, - "ConnectionState": { - "type": "string", - "enum": [ - "Invited", - "Negotiating", - "Connected" - ] - }, - "AgentEndpoint": { - "type": "object", - "properties": { - "did": { - "type": "string", - "readOnly": true - }, - "verkey": { - "type": "array", - "readOnly": true, - "items": { - "type": "string" - } - }, - "uri": { - "type": "string", - "readOnly": true - } - } - }, - "ConnectionContract": { - "type": "object", - "properties": { - "connectionId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "imageUrl": { - "type": "string" - }, - "myDid": { - "type": "string" - }, - "theirDid": { - "type": "string" - }, - "myKey": { - "type": "string" - }, - "theirKey": { - "type": "string" - }, - "state": { - "$ref": "#/definitions/ConnectionState" - }, - "invitation": { - "type": "string" - }, - "invitationUrl": { - "type": "string" - }, - "endpoint": { - "$ref": "#/definitions/AgentEndpoint" - }, - "createdAtUtc": { - "type": "string", - "format": "date-time" - }, - "multiParty": { - "type": "boolean" - } - } - }, - "ConnectionInvitationParameters": { - "type": "object", - "properties": { - "connectionId": { - "type": "string", - "description": "Unique connection identifier. If not specified, a random one will be generated." - }, - "multiParty": { - "type": "boolean", - "description": "If set to 'true', the invitation can be used by multiple parties and will always have the status set to 'Invited'.\r\nWhen a party accepts this invitation, a new connection record with a unique identifier will be created.\r\nDefault value is 'false'." - }, - "name": { - "type": "string", - "description": "Name that can be used as an alias for the connection.\r\nDefault value is 'null'." - } - }, - "description": "Connection invitation parameters" - }, - "CredentialState": { - "type": "string", - "enum": [ - "Offered", - "Requested", - "Issued", - "Rejected", - "Revoked" - ] - }, - "CredentialContract": { - "type": "object", - "properties": { - "credentialId": { - "type": "string" - }, - "state": { - "$ref": "#/definitions/CredentialState" - }, - "connectionId": { - "type": "string" - }, - "definitionId": { - "type": "string" - }, - "schemaId": { - "type": "string" - }, - "offerData": { - "type": "string" - }, - "offerUrl": { - "type": "string" - }, - "issuedAtUtc": { - "type": "string", - "format": "date-time" - }, - "acceptedAtUtc": { - "type": "string", - "format": "date-time" - }, - "values": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "correlationId": { - "type": "string" - } - } - }, - "CredentialOfferParameters": { - "type": "object", - "required": [ - "definitionId" - ], - "properties": { - "definitionId": { - "type": "string", - "description": "The credential definition identifier" - }, - "connectionId": { - "type": "string", - "description": "Connection identifier to send this credential to.\r\nIf omitted, the request will be treated as connectionless issuance and will generate a URL." - }, - "automaticIssuance": { - "type": "boolean", - "description": "If true, the credential will automatically be issued once the individual accepts the offer.\r\nIf false, when an individual accepts the offer the credential will be in state 'Requested' and must be manually issued.\r\nDefault value is 'false'." - }, - "credentialValues": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "description": "Send offer." - }, - "VerificationPolicyRestrictionAttribute": { - "type": "object", - "properties": { - "attributeName": { - "type": "string" - }, - "attributeValue": { - "type": "string" - } - } - }, - "VerificationPolicyRestriction": { - "type": "object", - "properties": { - "schemaId": { - "type": "string" - }, - "schemaIssuerDid": { - "type": "string" - }, - "schemaName": { - "type": "string" - }, - "schemaVersion": { - "type": "string" - }, - "issuerDid": { - "type": "string" - }, - "credentialDefinitionId": { - "type": "string" - }, - "value": { - "$ref": "#/definitions/VerificationPolicyRestrictionAttribute" - } - } - }, - "VerificationPolicyAttributeContract": { - "type": "object", - "required": [ - "attributeNames", - "policyName" - ], - "properties": { - "policyName": { - "type": "string" - }, - "attributeNames": { - "type": "array", - "items": { - "type": "string" - } - }, - "restrictions": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyRestriction" - } - } - } - }, - "VerificationPolicyPredicateContract": { - "type": "object", - "required": [ - "attributeName", - "policyName", - "predicateType", - "predicateValue" - ], - "properties": { - "policyName": { - "type": "string" - }, - "attributeName": { - "type": "string" - }, - "predicateType": { - "type": "string" - }, - "predicateValue": { - "type": "integer", - "format": "int32" - }, - "restrictions": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyRestriction" - } - } - } - }, - "VerificationPolicyRevocationRequirement": { - "type": "object", - "properties": { - "validAt": { - "type": "string", - "format": "date-time" - } - } - }, - "VerificationPolicyContract": { - "type": "object", - "required": [ - "name", - "version" - ], - "properties": { - "policyId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "attributes": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyAttributeContract" - } - }, - "predicates": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyPredicateContract" - } - }, - "revocationRequirement": { - "$ref": "#/definitions/VerificationPolicyRevocationRequirement" - } - } - }, - "VerificationPolicyParameters": { - "type": "object", - "required": [ - "name", - "version" - ], - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "attributes": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyAttributeContract" - } - }, - "predicates": { - "type": "array", - "items": { - "$ref": "#/definitions/VerificationPolicyPredicateContract" - } - }, - "revocationRequirement": { - "$ref": "#/definitions/VerificationPolicyRevocationRequirement" - } - } - }, - "CredentialDefinitionContract": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "attributes": { - "type": "array", - "items": { - "type": "string" - } - }, - "supportsRevocation": { - "type": "boolean" - }, - "schemaId": { - "type": "string" - }, - "definitionId": { - "type": "string" - } - } - }, - "CredentialDefinitionFromSchemaParameters": { - "type": "object", - "required": [ - "attributes", - "name", - "version" - ], - "properties": { - "name": { - "type": "string", - "description": "Schema name" - }, - "version": { - "type": "string", - "description": "Schema version" - }, - "attributes": { - "type": "array", - "description": "Schema attribute names", - "items": { - "type": "string" - } - }, - "supportRevocation": { - "type": "boolean", - "description": "Support credential revocation" - }, - "tag": { - "type": "string", - "description": "Unique tag to differentiate definitions of the same schema" - } - }, - "description": "Represents a request object to create new credential definition for an agency" - }, - "CredentialDefinitionParameters": { - "type": "object", - "properties": { - "supportRevocation": { - "type": "boolean", - "description": "Support credential revocation" - }, - "tag": { - "type": "string", - "description": "Unique tag to differentiate definitions of the same schema" - } - }, - "description": "Credential definition parameters" - }, - "SchemaParameters": { - "type": "object", - "required": [ - "attributeNames", - "name", - "version" - ], - "properties": { - "name": { - "type": "string", - "description": "The schema name" - }, - "version": { - "type": "string", - "description": "The schema version" - }, - "attributeNames": { - "type": "array", - "description": "The attribute names", - "items": { - "type": "string" - } - } - }, - "description": "Schema." - }, - "SchemaRecord": { - "type": "object", - "properties": { - "typeName": { - "type": "string", - "readOnly": true - }, - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "attributeNames": { - "type": "array", - "items": { - "type": "string" - } - }, - "id": { - "type": "string" - }, - "createdAtUtc": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "updatedAtUtc": { - "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "AttributeValue": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "AttributeFilter": { - "type": "object", - "properties": { - "schemaId": { - "type": "string" - }, - "schemaIssuerDid": { - "type": "string" - }, - "schemaName": { - "type": "string" - }, - "schemaVersion": { - "type": "string" - }, - "issuerDid": { - "type": "string" - }, - "credentialDefinitionId": { - "type": "string" - }, - "attributeValue": { - "$ref": "#/definitions/AttributeValue" - } - } - }, - "RevocationInterval": { - "type": "object", - "properties": { - "from": { - "type": "integer", - "format": "int32" - }, - "to": { - "type": "integer", - "format": "int32" - } - } - }, - "ProofAttributeInfo": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, - "restrictions": { - "type": "array", - "items": { - "$ref": "#/definitions/AttributeFilter" - } - }, - "nonRevoked": { - "$ref": "#/definitions/RevocationInterval" - } - } - }, - "ProofPredicateInfo": { - "type": "object", - "properties": { - "predicateType": { - "type": "string" - }, - "predicateValue": { - "type": "integer", - "format": "int32" - }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, - "restrictions": { - "type": "array", - "items": { - "$ref": "#/definitions/AttributeFilter" - } - }, - "nonRevoked": { - "$ref": "#/definitions/RevocationInterval" - } - } - }, - "ProofRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "nonce": { - "type": "string" - }, - "requestedAttributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ProofAttributeInfo" - } - }, - "requestedPredicates": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ProofPredicateInfo" - } - }, - "nonRevoked": { - "$ref": "#/definitions/RevocationInterval" - } - } - }, - "VerificationDefinitionContract": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The verification identifier" - }, - "proofRequest": { - "$ref": "#/definitions/ProofRequest" - } - }, - "description": "" - }, - "BasicMessageParameters": { - "type": "object", - "properties": { - "connectionId": { - "type": "string", - "description": "The connection identifier" - }, - "text": { - "type": "string", - "description": "The message text" - } - }, - "description": "Basic message parameters" - }, - "MessageDirection": { - "type": "string", - "enum": [ - "Incoming", - "Outgoing" - ] - }, - "BasicMessageRecord": { - "type": "object", - "properties": { - "typeName": { - "type": "string", - "readOnly": true - }, - "connectionId": { - "type": "string" - }, - "sentTime": { - "type": "string", - "format": "date-time" - }, - "direction": { - "$ref": "#/definitions/MessageDirection" - }, - "text": { - "type": "string" - }, - "id": { - "type": "string" - }, - "createdAtUtc": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "updatedAtUtc": { - "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "BasicMessageContract": { - "type": "object", - "properties": { - "connectionId": { - "type": "string", - "description": "The connection identifier" - }, - "timestamp": { - "type": "string", - "format": "date-time", - "description": "The message timestamp" - }, - "text": { - "type": "string", - "description": "The message text" - }, - "direction": { - "$ref": "#/definitions/MessageDirection" - } - }, - "description": "Basic message contract" - }, - "EndorserType": { - "type": "string", - "enum": [ - "Shared", - "Dedicated", - "Delegated" - ] - }, - "TenantExtendedInformationContract": { - "type": "object", - "properties": { - "issuerDid": { - "type": "string", - "description": "Issuer DID" - }, - "issuerKey": { - "type": "string", - "description": "Issuer Public Key" - }, - "issuerKeyGenerationSeed": { - "type": "string", - "description": "Issuer key generation seed used for deterministic key creation (32 characters)" - }, - "agentDid": { - "type": "string", - "description": "Agent DID" - }, - "agentKey": { - "type": "string", - "description": "Agent Public Key" - }, - "agentKeyGenerationSeed": { - "type": "string", - "description": "Agent key generation seed used for deterministic key creation (32 characters)" - }, - "agentServiceEndpoint": { - "type": "string", - "description": "Agent service endpoint URL" - }, - "transactionEndorsement": { - "$ref": "#/definitions/EndorserType" - } - }, - "description": "Extended tenant information" - }, - "TenantContract": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The tenant name", - "readOnly": true - }, - "imageUrl": { - "type": "string", - "description": "The image URL" - }, - "network": { - "$ref": "#/definitions/NetworkContract" - }, - "tenantId": { - "type": "string", - "description": "The tenant identifier", - "readOnly": true - }, - "extendedInformation": { - "$ref": "#/definitions/TenantExtendedInformationContract" - } - }, - "description": "Tenant info" - }, - "TenantParameters": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "issuerSeed": { - "type": "string", - "description": "Issuer seed used for deterministic DID generation.\r\nIf omitted, a random DID/Key is generated." - }, - "name": { - "type": "string", - "description": "The tenant name" - }, - "imageUrl": { - "type": "string", - "description": "URL of tenant profile image" - }, - "networkId": { - "type": "string", - "description": "Ledger network identifier.\r\nDefault is Sovrin Staging (sovrin-staging)." - }, - "endorserType": { - "$ref": "#/definitions/EndorserType" - } - }, - "description": "Configuration options for creating new tenant" - }, - "IssuerStatusContract": { - "type": "object", - "properties": { - "acceptanceText": { - "type": "string", - "description": "Transaction Author Agreement Text" - }, - "acceptanceVersion": { - "type": "string", - "description": "Transaction Author Agreement version" - }, - "acceptanceDigest": { - "type": "string", - "description": "The acceptance digest" - }, - "acceptanceTime": { - "type": "integer", - "format": "int64", - "description": "The acceptance time" - }, - "requireAcceptance": { - "type": "boolean", - "description": "Indicates if user needs to accept the latest agreement on the network" - }, - "transactionEndorsement": { - "$ref": "#/definitions/EndorserType" - }, - "issuerCanEndorse": { - "type": "boolean", - "description": "Indicates if the user has Endorser status" - } - }, - "description": "Issuer Status contract" - }, - "ProofState": { - "type": "string", - "enum": [ - "Proposed", - "Requested", - "Accepted", - "Rejected" - ] - }, - "ProofAttributeContract": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "revealed": { - "type": "boolean", - "readOnly": true - }, - "selfAttested": { - "type": "boolean" - }, - "conditional": { - "type": "boolean" - } - } - }, - "ProposedAttribute": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "credentialDefinitionId": { - "type": "string" - }, - "schemaId": { - "type": "string" - }, - "issuerDid": { - "type": "string" - }, - "mimeType": { - "type": "string" - }, - "value": { - "type": "string" - }, - "referent": { - "type": "string" - } - } - }, - "ProposedPredicate": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "credentialDefinitionId": { - "type": "string" - }, - "issuerDid": { - "type": "string" - }, - "schemaId": { - "type": "string" - }, - "predicate": { - "type": "string" - }, - "threshold": { - "type": "integer", - "format": "int32" - }, - "referent": { - "type": "string" - } - } - }, - "ProofProposal": { - "type": "object", - "properties": { - "comment": { - "type": "string" - }, - "proposedAttributes": { - "type": "array", - "items": { - "$ref": "#/definitions/ProposedAttribute" - } - }, - "proposedPredicates": { - "type": "array", - "items": { - "$ref": "#/definitions/ProposedPredicate" - } - } - } - }, - "VerificationContract": { - "type": "object", - "properties": { - "connectionId": { - "type": "string" - }, - "verificationId": { - "type": "string" - }, - "definitionId": { - "type": "string" - }, - "state": { - "$ref": "#/definitions/ProofState" - }, - "createdAtUtc": { - "type": "string", - "format": "date-time" - }, - "updatedAtUtc": { - "type": "string", - "format": "date-time" - }, - "isValid": { - "type": "boolean" - }, - "verifiedAtUtc": { - "type": "string", - "format": "date-time" - }, - "proof": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ProofAttributeContract" - } - }, - "policy": { - "$ref": "#/definitions/VerificationPolicyParameters" - }, - "proposal": { - "$ref": "#/definitions/ProofProposal" - }, - "verificationRequestData": { - "type": "string" - }, - "verificationRequestUrl": { - "type": "string" - } - } - }, - "VerificationParameters": { - "type": "object", - "required": [ - "verificationDefinitionId" - ], - "properties": { - "verificationDefinitionId": { - "type": "string", - "description": "Verification definition identifier" - }, - "connectionId": { - "type": "string", - "description": "Connection identifier" - } - }, - "description": "Create verification" - }, - "VerificationResult": { - "type": "object", - "properties": { - "valid": { - "type": "boolean", - "description": "True if verification passed, otherwise False" - }, - "proof": { - "type": "object", - "description": "Verification Proof Details", - "additionalProperties": { - "$ref": "#/definitions/ProofAttributeContract" - } - } - }, - "description": "Verification result" - }, - "WebhookType": { - "type": "string", - "enum": [ - "Notification", - "DelegatedEndorser", - "Zapier" - ] - }, - "WebhookContract": { - "type": "object", - "properties": { - "url": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/WebhookType" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "string" - } - } - }, - "WebhookParameters": { - "type": "object", - "properties": { - "url": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/WebhookType" - }, - "parameters": { - "type": "string" - } - } - } - } -} \ No newline at end of file diff --git a/infrastructure/local/run.sh b/infrastructure/local/run.sh index 3bc5e6987f..b9d016a8d8 100755 --- a/infrastructure/local/run.sh +++ b/infrastructure/local/run.sh @@ -125,5 +125,6 @@ fi PORT=${PORT} NETWORK=${NETWORK} DOCKERHOST=${DOCKERHOST} docker compose \ -p ${NAME} \ + ${DEBUG} \ -f ${SCRIPT_DIR}/../shared/docker-compose.yml \ --env-file ${ENV_FILE} ${DEBUG} up ${BACKGROUND} ${WAIT} diff --git a/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/DidCommX.scala b/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/DidCommX.scala index d81349731e..7e8b362eb3 100644 --- a/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/DidCommX.scala +++ b/mercury/agent-didcommx/src/main/scala/org/hyperledger/identus/mercury/DidCommX.scala @@ -55,26 +55,3 @@ class DidCommX() extends DidOps /* with DidAgent with DIDResolver */ { } yield (ret) } - -// object AgentService { -// val alice = ZLayer.succeed( -// AgentService[Agent.Alice.type]( -// new DIDComm(UniversalDidResolver, AliceSecretResolver.secretResolver), -// Agent.Alice -// ) -// ) -// val bob = ZLayer.succeed( -// AgentService[Agent.Bob.type]( -// new DIDComm(UniversalDidResolver, BobSecretResolver.secretResolver), -// Agent.Bob -// ) -// ) - -// // val charlie = ZLayer.succeed( -// // AgentService[Agent.Charlie.type]( -// // new DIDComm(UniversalDidResolver, CharlieSecretResolver.secretResolver), -// // Agent.Charlie -// // ) -// // ) - -// } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/oid4vci/CredentialConfiguration.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/oid4vci/CredentialConfiguration.scala new file mode 100644 index 0000000000..fe48297132 --- /dev/null +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/oid4vci/CredentialConfiguration.scala @@ -0,0 +1,20 @@ +package org.hyperledger.identus.pollux.core.model.oid4vci + +import org.hyperledger.identus.pollux.core.model.CredentialFormat + +import java.net.URI +import java.time.temporal.ChronoUnit +import java.time.Instant + +final case class CredentialConfiguration( + configurationId: String, + format: CredentialFormat, + schemaId: URI, + createdAt: Instant +) { + def scope: String = configurationId + + def withTruncatedTimestamp(unit: ChronoUnit = ChronoUnit.MICROS): CredentialConfiguration = copy( + createdAt = createdAt.truncatedTo(unit), + ) +} diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/oid4vci/CredentialIssuer.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/oid4vci/CredentialIssuer.scala new file mode 100644 index 0000000000..9392849f4f --- /dev/null +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/oid4vci/CredentialIssuer.scala @@ -0,0 +1,30 @@ +package org.hyperledger.identus.pollux.core.model.oid4vci + +import java.net.URL +import java.time.temporal.ChronoUnit +import java.time.Instant +import java.util.UUID + +case class CredentialIssuer( + id: UUID, + authorizationServer: URL, + authorizationServerClientId: String, + authorizationServerClientSecret: String, + createdAt: Instant, + updatedAt: Instant +) { + def withTruncatedTimestamp(unit: ChronoUnit = ChronoUnit.MICROS): CredentialIssuer = copy( + createdAt = createdAt.truncatedTo(unit), + updatedAt = updatedAt.truncatedTo(unit), + ) +} + +object CredentialIssuer { + def apply(authorizationServer: URL, clientId: String, clientSecret: String): CredentialIssuer = + apply(UUID.randomUUID(), authorizationServer, clientId, clientSecret) + + def apply(id: UUID, authorizationServer: URL, clientId: String, clientSecret: String): CredentialIssuer = { + val now = Instant.now + CredentialIssuer(id, authorizationServer, clientId, clientSecret, now, now).withTruncatedTimestamp() + } +} diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala index 2ec8445a2b..2486c17cf2 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchema.scala @@ -116,16 +116,22 @@ object CredentialSchema { given JsonEncoder[CredentialSchema] = DeriveJsonEncoder.gen[CredentialSchema] given JsonDecoder[CredentialSchema] = DeriveJsonDecoder.gen[CredentialSchema] + def resolveJWTSchema(uri: URI, uriDereferencer: URIDereferencer): IO[CredentialSchemaParsingError, Json] = { + for { + content <- uriDereferencer.dereference(uri).orDieAsUnmanagedFailure + json <- ZIO + .fromEither(content.fromJson[Json]) + .mapError(error => CredentialSchemaParsingError(error)) + } yield json + } + def validSchemaValidator( schemaId: String, uriDereferencer: URIDereferencer ): IO[InvalidURI | CredentialSchemaParsingError, JsonSchemaValidator] = { for { uri <- ZIO.attempt(new URI(schemaId)).mapError(_ => InvalidURI(schemaId)) - content <- uriDereferencer.dereference(uri).orDieAsUnmanagedFailure - json <- ZIO - .fromEither(content.fromJson[Json]) - .mapError(error => CredentialSchemaParsingError(error)) + json <- resolveJWTSchema(uri, uriDereferencer) schemaValidator <- JsonSchemaValidatorImpl .from(json) .orElse( diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/OID4VCIIssuerMetadataRepository.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/OID4VCIIssuerMetadataRepository.scala new file mode 100644 index 0000000000..a74d6641a7 --- /dev/null +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/OID4VCIIssuerMetadataRepository.scala @@ -0,0 +1,28 @@ +package org.hyperledger.identus.pollux.core.repository + +import org.hyperledger.identus.pollux.core.model.oid4vci.{CredentialConfiguration, CredentialIssuer} +import org.hyperledger.identus.shared.models.WalletAccessContext +import zio.* + +import java.net.URL +import java.util.UUID + +trait OID4VCIIssuerMetadataRepository { + def findIssuerById(issuerId: UUID): UIO[Option[CredentialIssuer]] + def createIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, Unit] + def findWalletIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] + def updateIssuer( + issuerId: UUID, + authorizationServer: Option[URL] = None, + authorizationServerClientId: Option[String] = None, + authorizationServerClientSecret: Option[String] = None + ): URIO[WalletAccessContext, Unit] + def deleteIssuer(issuerId: UUID): URIO[WalletAccessContext, Unit] + def createCredentialConfiguration(issuerId: UUID, config: CredentialConfiguration): URIO[WalletAccessContext, Unit] + def findCredentialConfigurationsByIssuer(issuerId: UUID): UIO[Seq[CredentialConfiguration]] + def findCredentialConfigurationById( + issuerId: UUID, + configurationId: String + ): URIO[WalletAccessContext, Option[CredentialConfiguration]] + def deleteCredentialConfiguration(issuerId: UUID, configurationId: String): URIO[WalletAccessContext, Unit] +} diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala index 6f784bad9c..f934e1ddbb 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/repository/PresentationRepositoryInMemory.scala @@ -143,14 +143,14 @@ class PresentationRepositoryInMemory( sdJwtClaimsToDisclose: Option[SdJwtCredentialToDisclose], protocolState: ProtocolState ): URIO[WalletAccessContext, Unit] = { - val result: URIO[WalletAccessContext, Unit] = { + val result = { for { storeRef <- walletStoreRef maybeRecord <- findPresentationRecord(recordId) result <- maybeRecord .map(record => for { - result <- storeRef.update(r => + _ <- storeRef.update(r => r.updated( recordId, record.copy( @@ -163,7 +163,7 @@ class PresentationRepositoryInMemory( ) ) ) - } yield result + } yield 1 ) .getOrElse(ZIO.succeed(0)) } yield result diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala index 910f932dd4..97a0e10119 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala @@ -3,6 +3,7 @@ package org.hyperledger.identus.pollux.core.service import io.circe.{Json, JsonObject} import io.circe.syntax.* import org.hyperledger.identus.castor.core.model.did.CanonicalPrismDID +import org.hyperledger.identus.castor.core.model.did.{CanonicalPrismDID, PrismDID, VerificationRelationship} import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.issuecredential.{ Attribute, @@ -13,6 +14,7 @@ import org.hyperledger.identus.mercury.protocol.issuecredential.{ import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError.* +import org.hyperledger.identus.pollux.vc.jwt.Issuer import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{Duration, IO, UIO, URIO, ZIO} @@ -150,6 +152,10 @@ trait CredentialService { failReason: Option[String] ): URIO[WalletAccessContext, Unit] + def getJwtIssuer( + jwtIssuerDID: PrismDID, + verificationRelationship: VerificationRelationship + ): URIO[WalletAccessContext, Issuer] } object CredentialService { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala index 3ac0291ff8..fafb8073fc 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala @@ -72,7 +72,7 @@ object CredentialServiceImpl { private val VC_JSON_SCHEMA_TYPE = "CredentialSchema2022" } -private class CredentialServiceImpl( +class CredentialServiceImpl( credentialRepository: CredentialRepository, credentialStatusListRepository: CredentialStatusListRepository, didResolver: DidResolver, @@ -503,7 +503,7 @@ private class CredentialServiceImpl( } yield keyId } - private def getJwtIssuer( + override def getJwtIssuer( jwtIssuerDID: PrismDID, verificationRelationship: VerificationRelationship ): URIO[WalletAccessContext, JwtIssuer] = { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala index 7bbcf5f2ff..38418d08d0 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala @@ -1,13 +1,14 @@ package org.hyperledger.identus.pollux.core.service import io.circe.Json -import org.hyperledger.identus.castor.core.model.did.CanonicalPrismDID +import org.hyperledger.identus.castor.core.model.did.{CanonicalPrismDID, PrismDID, VerificationRelationship} import org.hyperledger.identus.event.notification.* import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.issuecredential.{IssueCredential, OfferCredential, RequestCredential} import org.hyperledger.identus.pollux.core.model.{DidCommID, IssueCredentialRecord} import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError.* +import org.hyperledger.identus.pollux.vc.jwt.Issuer import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{Duration, UIO, URIO, URLayer, ZIO, ZLayer} @@ -221,6 +222,12 @@ class CredentialServiceNotifier( states: IssueCredentialRecord.ProtocolState* ): UIO[Seq[IssueCredentialRecord]] = svc.getIssueCredentialRecordsByStatesForAllWallets(ignoreWithZeroRetries, limit, states*) + + override def getJwtIssuer( + jwtIssuerDID: PrismDID, + verificationRelationship: VerificationRelationship + ): URIO[WalletAccessContext, Issuer] = + svc.getJwtIssuer(jwtIssuerDID, verificationRelationship) } object CredentialServiceNotifier { diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala index 3a7b096bc2..e928cbbb16 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala @@ -1,12 +1,13 @@ package org.hyperledger.identus.pollux.core.service import io.circe.Json -import org.hyperledger.identus.castor.core.model.did.CanonicalPrismDID +import org.hyperledger.identus.castor.core.model.did.{CanonicalPrismDID, PrismDID, VerificationRelationship} import org.hyperledger.identus.mercury.model.DidId import org.hyperledger.identus.mercury.protocol.issuecredential.{IssueCredential, OfferCredential, RequestCredential} import org.hyperledger.identus.pollux.core.model.{DidCommID, IssueCredentialRecord} import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError import org.hyperledger.identus.pollux.core.model.error.CredentialServiceError.* +import org.hyperledger.identus.pollux.vc.jwt.Issuer import org.hyperledger.identus.shared.models.WalletAccessContext import zio.{mock, Duration, IO, UIO, URIO, URLayer, ZIO, ZLayer} import zio.mock.{Mock, Proxy} @@ -274,6 +275,11 @@ object MockCredentialService extends Mock[CredentialService] { thid: DidCommID, ignoreWithZeroRetries: Boolean ): URIO[WalletAccessContext, Option[IssueCredentialRecord]] = ??? + + override def getJwtIssuer( + jwtIssuerDID: PrismDID, + verificationRelationship: VerificationRelationship + ): URIO[WalletAccessContext, Issuer] = ??? } } } diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockOID4VCIIssuerMetadataService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockOID4VCIIssuerMetadataService.scala new file mode 100644 index 0000000000..05e8fa9ce0 --- /dev/null +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/MockOID4VCIIssuerMetadataService.scala @@ -0,0 +1,79 @@ +package org.hyperledger.identus.pollux.core.service + +import org.hyperledger.identus.pollux.core.model.oid4vci.{CredentialConfiguration, CredentialIssuer} +import org.hyperledger.identus.pollux.core.model.CredentialFormat +import org.hyperledger.identus.shared.models.WalletAccessContext +import zio.* +import zio.mock.{Expectation, Mock, Proxy} +import zio.test.Assertion + +import java.net.URL +import java.util.UUID + +object MockOID4VCIIssuerMetadataService extends Mock[OID4VCIIssuerMetadataService] { + + import OID4VCIIssuerMetadataServiceError.* + + object GetCredentialConfigurationById + extends Effect[ + (UUID, String), + CredentialConfigurationNotFound, + CredentialConfiguration + ] + + override val compose: URLayer[mock.Proxy, OID4VCIIssuerMetadataService] = ZLayer { + ZIO.serviceWith[Proxy] { proxy => + new OID4VCIIssuerMetadataService { + override def getCredentialIssuer(issuerId: UUID): IO[IssuerIdNotFound, CredentialIssuer] = + ZIO.die(NotImplementedError()) + + override def createCredentialIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, CredentialIssuer] = + ZIO.die(NotImplementedError()) + + override def getCredentialIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] = + ZIO.die(NotImplementedError()) + + override def updateCredentialIssuer( + issuerId: UUID, + authorizationServer: Option[URL] = None, + authorizationServerClientId: Option[String] = None, + authorizationServerClientSecret: Option[String] = None + ): ZIO[WalletAccessContext, IssuerIdNotFound, CredentialIssuer] = ZIO.die(NotImplementedError()) + + override def deleteCredentialIssuer(issuerId: UUID): ZIO[WalletAccessContext, IssuerIdNotFound, Unit] = + ZIO.die(NotImplementedError()) + + override def createCredentialConfiguration( + issuerId: UUID, + format: CredentialFormat, + configurationId: String, + schemaId: String + ): ZIO[WalletAccessContext, InvalidSchemaId | UnsupportedCredentialFormat, CredentialConfiguration] = + ZIO.die(NotImplementedError()) + + override def getCredentialConfigurations( + issuerId: UUID + ): IO[IssuerIdNotFound, Seq[CredentialConfiguration]] = ZIO.die(NotImplementedError()) + + override def getCredentialConfigurationById( + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, CredentialConfigurationNotFound, CredentialConfiguration] = + proxy(GetCredentialConfigurationById, issuerId, configurationId) + + override def deleteCredentialConfiguration( + issuerId: UUID, + configurationId: String, + ): ZIO[WalletAccessContext, CredentialConfigurationNotFound, Unit] = ZIO.die(NotImplementedError()) + } + } + } + + def getCredentialConfigurationByIdExpectations( + configuration: CredentialConfiguration + ): Expectation[OID4VCIIssuerMetadataService] = + GetCredentialConfigurationById( + assertion = Assertion.anything, + result = Expectation.value(configuration) + ) +} diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataService.scala new file mode 100644 index 0000000000..9fe6e171fa --- /dev/null +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataService.scala @@ -0,0 +1,185 @@ +package org.hyperledger.identus.pollux.core.service + +import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.{CredentialSchemaParsingError, InvalidURI} +import org.hyperledger.identus.pollux.core.model.oid4vci.{CredentialConfiguration, CredentialIssuer} +import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema +import org.hyperledger.identus.pollux.core.model.CredentialFormat +import org.hyperledger.identus.pollux.core.repository.OID4VCIIssuerMetadataRepository +import org.hyperledger.identus.pollux.core.service.OID4VCIIssuerMetadataServiceError.{ + CredentialConfigurationNotFound, + InvalidSchemaId, + IssuerIdNotFound, + UnsupportedCredentialFormat +} +import org.hyperledger.identus.shared.db.Errors.UnexpectedAffectedRow +import org.hyperledger.identus.shared.models.{Failure, StatusCode, WalletAccessContext} +import zio.* + +import java.net.{URI, URL} +import java.util.UUID + +sealed trait OID4VCIIssuerMetadataServiceError( + val statusCode: StatusCode, + val userFacingMessage: String +) extends Failure { + override val namespace: String = "OID4VCIIssuerMetadataServiceError" +} + +object OID4VCIIssuerMetadataServiceError { + final case class IssuerIdNotFound(issuerId: UUID) + extends OID4VCIIssuerMetadataServiceError( + StatusCode.NotFound, + s"There is no credential issuer matching the provided identifier: issuerId=$issuerId" + ) + + final case class CredentialConfigurationNotFound(issuerId: UUID, configurationId: String) + extends OID4VCIIssuerMetadataServiceError( + StatusCode.NotFound, + s"There is no credential configuration matching the provided identifier: issuerId=$issuerId, configurationId=$configurationId" + ) + + final case class InvalidSchemaId(schemaId: String, msg: String) + extends OID4VCIIssuerMetadataServiceError( + StatusCode.BadRequest, + s"Invalid schemaId $schemaId. $msg" + ) + + final case class UnsupportedCredentialFormat(format: CredentialFormat) + extends OID4VCIIssuerMetadataServiceError( + StatusCode.BadRequest, + s"Unsupported credential format in OID4VCI protocol: $format" + ) +} + +trait OID4VCIIssuerMetadataService { + def getCredentialIssuer(issuerId: UUID): IO[IssuerIdNotFound, CredentialIssuer] + def createCredentialIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, CredentialIssuer] + def getCredentialIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] + def updateCredentialIssuer( + issuerId: UUID, + authorizationServer: Option[URL] = None, + authorizationServerClientId: Option[String] = None, + authorizationServerClientSecret: Option[String] = None + ): ZIO[WalletAccessContext, IssuerIdNotFound, CredentialIssuer] + def deleteCredentialIssuer(issuerId: UUID): ZIO[WalletAccessContext, IssuerIdNotFound, Unit] + def createCredentialConfiguration( + issuerId: UUID, + format: CredentialFormat, + configurationId: String, + schemaId: String + ): ZIO[WalletAccessContext, InvalidSchemaId | UnsupportedCredentialFormat, CredentialConfiguration] + def getCredentialConfigurations( + issuerId: UUID + ): IO[IssuerIdNotFound, Seq[CredentialConfiguration]] + def getCredentialConfigurationById( + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, CredentialConfigurationNotFound, CredentialConfiguration] + def deleteCredentialConfiguration( + issuerId: UUID, + configurationId: String, + ): ZIO[WalletAccessContext, CredentialConfigurationNotFound, Unit] +} + +class OID4VCIIssuerMetadataServiceImpl(repository: OID4VCIIssuerMetadataRepository, uriDereferencer: URIDereferencer) + extends OID4VCIIssuerMetadataService { + + override def createCredentialIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, CredentialIssuer] = + repository.createIssuer(issuer).as(issuer) + + override def getCredentialIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] = + repository.findWalletIssuers + + override def getCredentialIssuer(issuerId: UUID): IO[IssuerIdNotFound, CredentialIssuer] = + repository + .findIssuerById(issuerId) + .someOrFail(IssuerIdNotFound(issuerId)) + + override def updateCredentialIssuer( + issuerId: UUID, + authorizationServer: Option[URL], + authorizationServerClientId: Option[String], + authorizationServerClientSecret: Option[String] + ): ZIO[WalletAccessContext, IssuerIdNotFound, CredentialIssuer] = + for { + _ <- repository + .updateIssuer( + issuerId = issuerId, + authorizationServer = authorizationServer, + authorizationServerClientId = authorizationServerClientId, + authorizationServerClientSecret = authorizationServerClientSecret + ) + .catchSomeDefect { case _: UnexpectedAffectedRow => + ZIO.fail(IssuerIdNotFound(issuerId)) + } + updatedIssuer <- getCredentialIssuer(issuerId) + } yield updatedIssuer + + override def deleteCredentialIssuer(issuerId: UUID): ZIO[WalletAccessContext, IssuerIdNotFound, Unit] = + repository + .deleteIssuer(issuerId) + .catchSomeDefect { case _: UnexpectedAffectedRow => + ZIO.fail(IssuerIdNotFound(issuerId)) + } + + override def createCredentialConfiguration( + issuerId: UUID, + format: CredentialFormat, + configurationId: String, + schemaId: String + ): ZIO[WalletAccessContext, InvalidSchemaId | UnsupportedCredentialFormat, CredentialConfiguration] = { + for { + _ <- format match { + case CredentialFormat.JWT => ZIO.unit + case f => ZIO.fail(UnsupportedCredentialFormat(f)) + } + schemaUri <- ZIO.attempt(new URI(schemaId)).mapError(t => InvalidSchemaId(schemaId, t.getMessage)) + _ <- CredentialSchema + .validSchemaValidator(schemaUri.toString(), uriDereferencer) + .catchAll { + case e: InvalidURI => ZIO.fail(InvalidSchemaId(schemaId, e.userFacingMessage)) + case e: CredentialSchemaParsingError => ZIO.fail(InvalidSchemaId(schemaId, e.cause)) + } + now <- ZIO.clockWith(_.instant) + config = CredentialConfiguration( + configurationId = configurationId, + format = CredentialFormat.JWT, + schemaId = schemaUri, + createdAt = now + ) + _ <- repository.createCredentialConfiguration(issuerId, config) + } yield config + } + + override def getCredentialConfigurations( + issuerId: UUID + ): IO[IssuerIdNotFound, Seq[CredentialConfiguration]] = + repository + .findIssuerById(issuerId) + .someOrFail(IssuerIdNotFound(issuerId)) + .flatMap(_ => repository.findCredentialConfigurationsByIssuer(issuerId)) + + override def getCredentialConfigurationById( + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, CredentialConfigurationNotFound, CredentialConfiguration] = + repository + .findCredentialConfigurationById(issuerId, configurationId) + .someOrFail(CredentialConfigurationNotFound(issuerId, configurationId)) + + override def deleteCredentialConfiguration( + issuerId: UUID, + configurationId: String + ): ZIO[WalletAccessContext, CredentialConfigurationNotFound, Unit] = + repository + .deleteCredentialConfiguration(issuerId, configurationId) + .catchSomeDefect { case _: UnexpectedAffectedRow => + ZIO.fail(CredentialConfigurationNotFound(issuerId, configurationId)) + } +} + +object OID4VCIIssuerMetadataServiceImpl { + def layer: URLayer[OID4VCIIssuerMetadataRepository & URIDereferencer, OID4VCIIssuerMetadataService] = { + ZLayer.fromFunction(OID4VCIIssuerMetadataServiceImpl(_, _)) + } +} diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/OID4VCIIssuerMetadataRepositorySpecSuite.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/OID4VCIIssuerMetadataRepositorySpecSuite.scala new file mode 100644 index 0000000000..26134ff767 --- /dev/null +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/repository/OID4VCIIssuerMetadataRepositorySpecSuite.scala @@ -0,0 +1,292 @@ +package org.hyperledger.identus.pollux.core.repository + +import org.hyperledger.identus.pollux.core.model.oid4vci.{CredentialConfiguration, CredentialIssuer} +import org.hyperledger.identus.pollux.core.model.CredentialFormat +import org.hyperledger.identus.shared.db.Errors.UnexpectedAffectedRow +import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import zio.{ZIO, ZLayer} +import zio.test.* +import zio.test.Assertion.* + +import java.net.{URI, URL} +import java.time.Instant + +object OID4VCIIssuerMetadataRepositorySpecSuite { + + private val credConfig = CredentialConfiguration( + configurationId = "DrivingLicense", + format = CredentialFormat.JWT, + schemaId = URI.create("http://example.com/schema"), + createdAt = Instant.now + ).withTruncatedTimestamp() + + private def makeCredentialIssuer(authorizationServer: URL): CredentialIssuer = CredentialIssuer( + authorizationServer = authorizationServer, + clientId = "client", + clientSecret = "secret" + ) + + private def initMultiWalletIssuers = { + val walletId1 = WalletId.random + val walletId2 = WalletId.random + val wallet1 = ZLayer.succeed(WalletAccessContext(walletId1)) + val wallet2 = ZLayer.succeed(WalletAccessContext(walletId2)) + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + authServer2 = URI.create("http://example-2.com").toURL() + issuer1 = makeCredentialIssuer(authorizationServer = authServer1) + issuer2 = makeCredentialIssuer(authorizationServer = authServer2) + _ <- repo.createIssuer(issuer1).provide(wallet1) + _ <- repo.createIssuer(issuer2).provide(wallet2) + } yield (issuer1, wallet1, issuer2, wallet2) + } + + val testSuite = suite("CRUD operations")( + test("find non-existing credential issuers return None") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + issuerId <- ZIO.randomWith(_.nextUUID) + maybeIssuer <- repo.findIssuerById(issuerId) + } yield assert(maybeIssuer)(isNone) + }, + test("create credential issuers successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + authServer2 = URI.create("http://example-2.com").toURL() + issuer1 = makeCredentialIssuer(authorizationServer = authServer1) + issuer2 = makeCredentialIssuer(authorizationServer = authServer2) + _ <- repo.createIssuer(issuer1) + _ <- repo.createIssuer(issuer2) + maybeIssuer1 <- repo.findIssuerById(issuer1.id) + maybeIssuer2 <- repo.findIssuerById(issuer2.id) + } yield assert(maybeIssuer1)(isSome(equalTo(issuer1))) && + assert(maybeIssuer2)(isSome(equalTo(issuer2))) + }, + test("create credential issuers with same id should fail") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + issuer1 = makeCredentialIssuer(authorizationServer = authServer1) + _ <- repo.createIssuer(issuer1) + exit <- repo.createIssuer(issuer1).exit + } yield assert(exit)(dies(anything)) + }, + test("delete credential issuer successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + _ <- repo.createIssuer(issuer) + _ <- repo.deleteIssuer(issuer.id) + maybeIssuer <- repo.findIssuerById(issuer.id) + } yield assert(maybeIssuer)(isNone) + }, + test("delete non-existing credential issuer should fail") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + exit <- repo.deleteIssuer(issuer.id).exit + } yield assert(exit)(dies(isSubtype[UnexpectedAffectedRow](anything))) + }, + test("update credential issuer successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + authServer2 = URI.create("http://example-2.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer1) + _ <- repo.createIssuer(issuer) + _ <- repo.updateIssuer( + issuerId = issuer.id, + authorizationServer = Some(authServer2), + authorizationServerClientId = Some("client-2"), + authorizationServerClientSecret = Some("secret-2") + ) + updatedIssuer <- repo.findIssuerById(issuer.id).some + } yield assert(updatedIssuer.id)(equalTo(issuer.id)) && + assert(updatedIssuer.authorizationServer)(equalTo(authServer2)) && + assert(updatedIssuer.authorizationServerClientId)(equalTo("client-2")) && + assert(updatedIssuer.authorizationServerClientSecret)(equalTo("secret-2")) && + assert(updatedIssuer.updatedAt)(not(equalTo(issuer.createdAt))) + }, + test("update credential issuer with empty patch successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + authServer2 = URI.create("http://example-2.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer1) + _ <- repo.createIssuer(issuer) + _ <- repo.updateIssuer(issuer.id) // empty patch + updatedIssuer <- repo.findIssuerById(issuer.id).some + } yield assert(updatedIssuer.id)(equalTo(issuer.id)) && + assert(updatedIssuer.authorizationServer)(equalTo(issuer.authorizationServer)) && + assert(updatedIssuer.createdAt)(equalTo(issuer.createdAt)) + }, + test("update non-existing credential issuer should fail") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + issuerId <- ZIO.randomWith(_.nextUUID) + authServer = URI.create("http://example-1.com").toURL() + exit <- repo.updateIssuer(issuerId, authorizationServer = Some(authServer)).exit + } yield assert(exit)(dies(isSubtype[UnexpectedAffectedRow](anything))) + }, + test("create credential configuration successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + _ <- repo.createIssuer(issuer) + _ <- repo.createCredentialConfiguration(issuer.id, credConfig) + maybeCredConfig <- repo.findCredentialConfigurationById(issuer.id, credConfig.configurationId) + } yield assert(maybeCredConfig)(isSome(equalTo(credConfig))) + }, + test("create credential configuration with same id should fail") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer).withTruncatedTimestamp() + _ <- repo.createIssuer(issuer) + _ <- repo.createCredentialConfiguration(issuer.id, credConfig) + exit <- repo.createCredentialConfiguration(issuer.id, credConfig).exit + } yield assert(exit)(dies(anything)) + }, + test("create credential configuration with same id for different issuer successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + authServer2 = URI.create("http://example-2.com").toURL() + issuer1 = makeCredentialIssuer(authorizationServer = authServer1) + issuer2 = makeCredentialIssuer(authorizationServer = authServer2) + _ <- repo.createIssuer(issuer1) + _ <- repo.createIssuer(issuer2) + _ <- repo.createCredentialConfiguration(issuer1.id, credConfig) + _ <- repo.createCredentialConfiguration(issuer2.id, credConfig) + maybeCredConfig1 <- repo.findCredentialConfigurationById(issuer1.id, credConfig.configurationId) + maybeCredConfig2 <- repo.findCredentialConfigurationById(issuer2.id, credConfig.configurationId) + } yield assert(maybeCredConfig1)(isSome(equalTo(credConfig))) && + assert(maybeCredConfig2)(isSome(equalTo(credConfig))) + }, + test("create credential configuration for non-existing issuer should fail") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + issuerId <- ZIO.randomWith(_.nextUUID) + exit <- repo.createCredentialConfiguration(issuerId, credConfig).exit + } yield assert(exit)(dies(anything)) + }, + test("list credential configurations successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer1 = URI.create("http://example-1.com").toURL() + issuer1 = makeCredentialIssuer(authorizationServer = authServer1) + issuer2 = makeCredentialIssuer(authorizationServer = authServer1) + credConfig1 = credConfig.copy(configurationId = "DrivingLicense") + credConfig2 = credConfig.copy(configurationId = "UniversityDegree") + credConfig3 = credConfig.copy(configurationId = "TrainingCertificate") + _ <- repo.createIssuer(issuer1) + _ <- repo.createIssuer(issuer2) + _ <- repo.createCredentialConfiguration(issuer1.id, credConfig1) + _ <- repo.createCredentialConfiguration(issuer1.id, credConfig2) + _ <- repo.createCredentialConfiguration(issuer2.id, credConfig3) + credConfigs1 <- repo.findCredentialConfigurationsByIssuer(issuer1.id) + credConfigs2 <- repo.findCredentialConfigurationsByIssuer(issuer2.id) + } yield assert(credConfigs1)(hasSameElements(Seq(credConfig1, credConfig2))) && + assert(credConfigs2)(hasSameElements(Seq(credConfig3))) + }, + test("find and list return empty result for non-existing configurations") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + _ <- repo.createIssuer(issuer) + maybeCredConfig <- repo.findCredentialConfigurationById(issuer.id, credConfig.configurationId) + credConfigs <- repo.findCredentialConfigurationsByIssuer(issuer.id) + } yield assert(maybeCredConfig)(isNone) && + assert(credConfigs)(isEmpty) + }, + test("delete credential configuration successfully") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + _ <- repo.createIssuer(issuer) + _ <- repo.createCredentialConfiguration(issuer.id, credConfig) + maybeCredConfig1 <- repo.findCredentialConfigurationById(issuer.id, credConfig.configurationId) + _ <- repo.deleteCredentialConfiguration(issuer.id, credConfig.configurationId) + maybeCredConfig2 <- repo.findCredentialConfigurationById(issuer.id, credConfig.configurationId) + } yield assert(maybeCredConfig1)(isSome(equalTo(credConfig))) && + assert(maybeCredConfig2)(isNone) + }, + test("delete non-existing creential configuration should fail") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + randomId <- ZIO.randomWith(_.nextUUID) + _ <- repo.createIssuer(issuer) + _ <- repo.createCredentialConfiguration(issuer.id, credConfig) + exit1 <- repo.deleteCredentialConfiguration(issuer.id, "ExampleLicense").exit + exit2 <- repo.deleteCredentialConfiguration(randomId, "ExampleLicense").exit + } yield assert(exit1)(diesWithA[UnexpectedAffectedRow]) && + assert(exit2)(diesWithA[UnexpectedAffectedRow]) + }, + test("delete issuer also delete credential configuration") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + authServer = URI.create("http://example-1.com").toURL() + issuer = makeCredentialIssuer(authorizationServer = authServer) + _ <- repo.createIssuer(issuer) + _ <- repo.createCredentialConfiguration(issuer.id, credConfig) + _ <- repo.deleteIssuer(issuer.id) + maybeCredConfig <- repo.findCredentialConfigurationById(issuer.id, credConfig.configurationId) + } yield assert(maybeCredConfig)(isNone) + } + ).provideSomeLayer(ZLayer.succeed(WalletAccessContext(WalletId.random))) + + val multitenantTestSuite = suite("multi-tenant CRUD operation")( + test("list only issuers inside wallet") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + init <- initMultiWalletIssuers + (issuer1, wallet1, issuer2, wallet2) = init + issuers1 <- repo.findWalletIssuers.provide(wallet1) + issuers2 <- repo.findWalletIssuers.provide(wallet2) + } yield assert(issuers1)(hasSameElements(Seq(issuer1))) && + assert(issuers2)(hasSameElements(Seq(issuer2))) + }, + test("update issuer across wallet is not allowed") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + init <- initMultiWalletIssuers + (issuer1, wallet1, issuer2, wallet2) = init + exit <- repo.updateIssuer(issuer1.id).provide(wallet2).exit + } yield assert(exit)(diesWithA[UnexpectedAffectedRow]) + }, + test("delete issuer across wallet is not allowed") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + init <- initMultiWalletIssuers + (issuer1, wallet1, issuer2, wallet2) = init + exit <- repo.deleteIssuer(issuer1.id).provide(wallet2).exit + } yield assert(exit)(diesWithA[UnexpectedAffectedRow]) + }, + test("create credential configuration across wallet is not allowed") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + init <- initMultiWalletIssuers + (issuer1, wallet1, issuer2, wallet2) = init + exit <- repo.createCredentialConfiguration(issuer1.id, credConfig).provide(wallet2).exit + } yield assert(exit)(dies(anything)) + }, + test("delete credential configuration across wallet is not allowed") { + for { + repo <- ZIO.service[OID4VCIIssuerMetadataRepository] + init <- initMultiWalletIssuers + (issuer1, wallet1, issuer2, wallet2) = init + exit <- repo.deleteCredentialConfiguration(issuer1.id, credConfig.configurationId).provide(wallet2).exit + } yield assert(exit)(diesWithA[UnexpectedAffectedRow]) + }, + ) + +} diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala index 722376ebb8..5b8df2ac05 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala @@ -28,8 +28,7 @@ trait CredentialServiceSpecHelper { protected val defaultWalletLayer = ZLayer.succeed(WalletAccessContext(WalletId.default)) protected val credentialDefinitionServiceLayer = - CredentialDefinitionRepositoryInMemory.layer ++ ResourceURIDereferencerImpl.layer >>> - CredentialDefinitionServiceImpl.layer ++ defaultWalletLayer + CredentialDefinitionRepositoryInMemory.layer >>> CredentialDefinitionServiceImpl.layer protected val credentialServiceLayer : URLayer[DIDService & ManagedDIDService & URIDereferencer, CredentialService & CredentialDefinitionService] = diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataServiceSpecSuite.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataServiceSpecSuite.scala new file mode 100644 index 0000000000..8d2d3614bb --- /dev/null +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataServiceSpecSuite.scala @@ -0,0 +1,124 @@ +package org.hyperledger.identus.pollux.core.service + +import org.hyperledger.identus.pollux.core.model.oid4vci.CredentialIssuer +import org.hyperledger.identus.pollux.core.model.CredentialFormat +import org.hyperledger.identus.pollux.core.service.OID4VCIIssuerMetadataServiceError.{ + CredentialConfigurationNotFound, + InvalidSchemaId, + IssuerIdNotFound +} +import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} +import zio.{ZIO, ZLayer} +import zio.test.* +import zio.test.Assertion.* + +import java.net.{URI, URL} + +object OID4VCIIssuerMetadataServiceSpecSuite { + + private def makeCredentialIssuer(authorizationServer: URL): CredentialIssuer = CredentialIssuer( + authorizationServer = authorizationServer, + clientId = "client", + clientSecret = "secret" + ) + + val testSuite = suite("OID4VCIssuerMetadataService")( + test("get credential issuer successfully") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + authServer1 = URI.create("http://example-1.com").toURL() + authServer2 = URI.create("http://example-2.com").toURL() + issuer1 <- service.createCredentialIssuer(makeCredentialIssuer(authServer1)) + issuer2 <- service.createCredentialIssuer(makeCredentialIssuer(authServer2)) + getIssuer1 <- service.getCredentialIssuer(issuer1.id) + getIssuer2 <- service.getCredentialIssuer(issuer2.id) + getIssuers <- service.getCredentialIssuers + } yield assert(getIssuer1)(equalTo(issuer1)) && + assert(getIssuer2)(equalTo(issuer2)) && + assert(getIssuers)(hasSameElements(Seq(issuer1, issuer2))) + }, + test("get non-existing credential issuer should fail") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + issuerId <- ZIO.randomWith(_.nextUUID) + issuers <- service.getCredentialIssuers + exit <- service.getCredentialIssuer(issuerId).exit + } yield assert(exit)(failsWithA[IssuerIdNotFound]) && + assert(issuers)(isEmpty) + }, + test("update credential issuer successfully") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + authServer = URI.create("http://example-1.com").toURL() + issuer <- service.createCredentialIssuer(makeCredentialIssuer(authServer)) + updatedAuthServer = URI.create("http://example-2.com").toURL() + _ <- service.updateCredentialIssuer(issuer.id, authorizationServer = Some(updatedAuthServer)) + updatedIssuer <- service.getCredentialIssuer(issuer.id) + } yield assert(updatedIssuer.authorizationServer)(equalTo(updatedAuthServer)) + }, + test("update non-existing credential issuer should fail") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + issuerId <- ZIO.randomWith(_.nextUUID) + exit <- service.updateCredentialIssuer(issuerId, Some(URI.create("http://example.com").toURL())).exit + } yield assert(exit)(failsWithA[IssuerIdNotFound]) + }, + test("create credential configuration successfully") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + authServer = URI.create("http://example-1.com").toURL() + issuer <- service.createCredentialIssuer(makeCredentialIssuer(authServer)) + _ <- service + .createCredentialConfiguration( + issuer.id, + CredentialFormat.JWT, + "UniversityDegree", + "resource:///vc-schema-example.json" + ) + credConfig <- service.getCredentialConfigurationById(issuer.id, "UniversityDegree") + } yield assert(credConfig.configurationId)(equalTo("UniversityDegree")) && + assert(credConfig.format)(equalTo(CredentialFormat.JWT)) && + assert(credConfig.schemaId)(equalTo(URI.create("resource:///vc-schema-example.json"))) + }, + test("create credential configuration check for schemaId validity") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + authServer = URI.create("http://example-1.com").toURL() + issuer <- service.createCredentialIssuer(makeCredentialIssuer(authServer)) + createCredConfig = (schemaId: String) => + service + .createCredentialConfiguration( + issuer.id, + CredentialFormat.JWT, + "UniversityDegree", + schemaId + ) + exit1 <- createCredConfig("not a uri").exit + exit2 <- createCredConfig("http://localhost/schema").exit + } yield assert(exit1)(failsWithA[InvalidSchemaId]) && + assert(exit2)(dies(anything)) + }, + test("list credential configurations for non-existing issuer should fail") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + issuerId <- ZIO.randomWith(_.nextUUID) + exit <- service.getCredentialConfigurations(issuerId).exit + } yield assert(exit)(failsWithA[IssuerIdNotFound]) + }, + test("get non-existing credential configuration should fail") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + issuerId <- ZIO.randomWith(_.nextUUID) + exit <- service.getCredentialConfigurationById(issuerId, "UniversityDegree").exit + } yield assert(exit)(failsWithA[CredentialConfigurationNotFound]) + }, + test("delete non-existing credential configuration should fail") { + for { + service <- ZIO.service[OID4VCIIssuerMetadataService] + issuerId <- ZIO.randomWith(_.nextUUID) + exit <- service.deleteCredentialConfiguration(issuerId, "UniversityDegree").exit + } yield assert(exit)(failsWithA[CredentialConfigurationNotFound]) + }, + ).provideSomeLayer(ZLayer.succeed(WalletAccessContext(WalletId.random))) + +} diff --git a/pollux/sql-doobie/src/main/resources/sql/pollux/V21__add_issuer_metadata.sql b/pollux/sql-doobie/src/main/resources/sql/pollux/V21__add_issuer_metadata.sql new file mode 100644 index 0000000000..369ba7ce1a --- /dev/null +++ b/pollux/sql-doobie/src/main/resources/sql/pollux/V21__add_issuer_metadata.sql @@ -0,0 +1,38 @@ +CREATE TABLE public.issuer_metadata ( + id UUID PRIMARY KEY, + authorization_server VARCHAR(500) NOT NULL, + authorization_server_client_id VARCHAR(100) NOT NULL, + authorization_server_client_secret VARCHAR(100) NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE NOT NULL, + wallet_id UUID NOT NULL +); + +CREATE TABLE public.issuer_credential_configuration ( + configuration_id VARCHAR(100) NOT NULL, + issuer_id UUID NOT NULL, + format VARCHAR(9) NOT NULL, + schema_id VARCHAR(500) NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + UNIQUE (configuration_id, issuer_id), + CONSTRAINT fk_issuer FOREIGN KEY (issuer_id) REFERENCES public.issuer_metadata(id) ON DELETE CASCADE +); + +ALTER TABLE public.issuer_metadata + ENABLE ROW LEVEL SECURITY; + +ALTER TABLE public.issuer_credential_configuration + ENABLE ROW LEVEL SECURITY; + +CREATE POLICY issuer_metadata_wallet_isolation + ON public.issuer_metadata + USING (wallet_id = current_setting('app.current_wallet_id')::UUID); + +CREATE POLICY issuer_credential_configuration_wallet_isolation + ON public.issuer_credential_configuration + USING ( + EXISTS (SELECT 1 + FROM public.issuer_metadata AS im + WHERE im.wallet_id = current_setting('app.current_wallet_id')::UUID + AND im.id = public.issuer_credential_configuration.issuer_id) + ); diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/Implicits.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/Implicits.scala index 10064bf2e5..db884407a3 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/Implicits.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/Implicits.scala @@ -4,14 +4,12 @@ import doobie.util.{Get, Put} import org.hyperledger.identus.castor.core.model.did.{CanonicalPrismDID, PrismDID} import org.hyperledger.identus.pollux.core.model.* import org.hyperledger.identus.pollux.vc.jwt.StatusPurpose -import org.hyperledger.identus.shared.models.WalletId + +import java.net.{URI, URL} given didCommIDGet: Get[DidCommID] = Get[String].map(DidCommID(_)) given didCommIDPut: Put[DidCommID] = Put[String].contramap(_.value) -given walletIdGet: Get[WalletId] = Get[String].map(WalletId.fromUUIDString) -given walletIdPut: Put[WalletId] = Put[String].contramap(_.toString) - given prismDIDGet: Get[CanonicalPrismDID] = Get[String].map(s => PrismDID.fromString(s).fold(e => throw RuntimeException(e), _.asCanonical)) given prismDIDPut: Put[CanonicalPrismDID] = Put[String].contramap(_.toString) @@ -26,3 +24,12 @@ given statusPurposePut: Put[StatusPurpose] = Put[String].contramap { case StatusPurpose.Revocation => StatusPurpose.Revocation.str case StatusPurpose.Suspension => StatusPurpose.Suspension.str } + +given urlGet: Get[URL] = Get[String].map(s => URI.create(s).toURL()) +given urlPut: Put[URL] = Put[String].contramap(_.toString()) + +given uriGet: Get[URI] = Get[String].map(s => URI.create(s)) +given uriPut: Put[URI] = Put[String].contramap(_.toString()) + +given credFormatGet: Get[CredentialFormat] = Get[String].map(CredentialFormat.valueOf) +given credFormatPut: Put[CredentialFormat] = Put[String].contramap(_.toString()) diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialStatusListRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialStatusListRepository.scala index 57c5fea44c..177d44dd59 100644 --- a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialStatusListRepository.scala +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcCredentialStatusListRepository.scala @@ -12,6 +12,7 @@ import org.hyperledger.identus.pollux.vc.jwt.revocation.{BitString, BitStringErr import org.hyperledger.identus.pollux.vc.jwt.revocation.BitStringError.* import org.hyperledger.identus.shared.db.ContextAwareTask import org.hyperledger.identus.shared.db.Implicits.* +import org.hyperledger.identus.shared.db.Implicits.{*, given} import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId} import zio.* import zio.interop.catz.* diff --git a/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcOID4VCIIssuerMetadataRepository.scala b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcOID4VCIIssuerMetadataRepository.scala new file mode 100644 index 0000000000..b43fe8f70e --- /dev/null +++ b/pollux/sql-doobie/src/main/scala/org/hyperledger/identus/pollux/sql/repository/JdbcOID4VCIIssuerMetadataRepository.scala @@ -0,0 +1,210 @@ +package org.hyperledger.identus.pollux.sql.repository + +import doobie.* +import doobie.implicits.* +import doobie.postgres.implicits.* +import doobie.util.transactor.Transactor +import org.hyperledger.identus.pollux.core.model.oid4vci.{CredentialConfiguration, CredentialIssuer} +import org.hyperledger.identus.pollux.core.repository.OID4VCIIssuerMetadataRepository +import org.hyperledger.identus.shared.db.ContextAwareTask +import org.hyperledger.identus.shared.db.Implicits.* +import org.hyperledger.identus.shared.models.WalletAccessContext +import zio.* +import zio.interop.catz.* + +import java.net.URL +import java.time.Instant +import java.util.UUID + +class JdbcOID4VCIIssuerMetadataRepository(xa: Transactor[ContextAwareTask], xb: Transactor[Task]) + extends OID4VCIIssuerMetadataRepository { + + override def findIssuerById(issuerId: UUID): UIO[Option[CredentialIssuer]] = { + val cxnIO = sql""" + |SELECT + | id, + | authorization_server, + | authorization_server_client_id, + | authorization_server_client_secret, + | created_at, + | updated_at + |FROM public.issuer_metadata + |WHERE id = $issuerId + """.stripMargin + .query[CredentialIssuer] + .option + + cxnIO + .transact(xb) + .orDie + } + + override def findWalletIssuers: URIO[WalletAccessContext, Seq[CredentialIssuer]] = { + val cxnIO = sql""" + |SELECT + | id, + | authorization_server, + | authorization_server_client_id, + | authorization_server_client_secret, + | created_at, + | updated_at + |FROM public.issuer_metadata + """.stripMargin + .query[CredentialIssuer] + .to[Seq] + + cxnIO + .transactWallet(xa) + .orDie + } + + override def createIssuer(issuer: CredentialIssuer): URIO[WalletAccessContext, Unit] = { + val cxnIO = sql""" + |INSERT INTO public.issuer_metadata ( + | id, + | authorization_server, + | authorization_server_client_id, + | authorization_server_client_secret, + | created_at, + | updated_at, + | wallet_id + |) VALUES ( + | ${issuer.id}, + | ${issuer.authorizationServer}, + | ${issuer.authorizationServerClientId}, + | ${issuer.authorizationServerClientSecret}, + | ${issuer.createdAt}, + | ${issuer.updatedAt}, + | current_setting('app.current_wallet_id')::UUID + |) + """.stripMargin.update + + cxnIO.run + .transactWallet(xa) + .ensureOneAffectedRowOrDie + } + + override def updateIssuer( + issuerId: UUID, + authorizationServer: Option[URL], + authorizationServerClientId: Option[String], + authorizationServerClientSecret: Option[String] + ): URIO[WalletAccessContext, Unit] = { + val setFr = (now: Instant) => + Fragments.set( + fr"updated_at = $now", + (Seq( + authorizationServer.map(url => fr"authorization_server = $url"), + authorizationServerClientId.map(i => fr"authorization_server_client_id = $i"), + authorizationServerClientSecret.map(i => fr"authorization_server_client_secret = $i") + ).flatten)* + ) + val cxnIO = (setFr: Fragment) => sql""" + |UPDATE public.issuer_metadata + |$setFr + |WHERE id = $issuerId + """.stripMargin.update + + for { + now <- ZIO.clockWith(_.instant) + _ <- cxnIO(setFr(now)).run + .transactWallet(xa) + .ensureOneAffectedRowOrDie + } yield () + } + + override def deleteIssuer(issuerId: UUID): URIO[WalletAccessContext, Unit] = { + val cxnIO = sql""" + | DELETE FROM public.issuer_metadata + | WHERE id = $issuerId + """.stripMargin.update + + cxnIO.run + .transactWallet(xa) + .ensureOneAffectedRowOrDie + } + + override def createCredentialConfiguration( + issuerId: UUID, + config: CredentialConfiguration + ): URIO[WalletAccessContext, Unit] = { + val cxnIO = sql""" + |INSERT INTO public.issuer_credential_configuration ( + | configuration_id, + | issuer_id, + | format, + | schema_id, + | created_at + |) VALUES ( + | ${config.configurationId}, + | ${issuerId}, + | ${config.format}, + | ${config.schemaId}, + | ${config.createdAt} + |) + """.stripMargin.update + + cxnIO.run + .transactWallet(xa) + .ensureOneAffectedRowOrDie + } + + override def findCredentialConfigurationsByIssuer(issuerId: UUID): UIO[Seq[CredentialConfiguration]] = { + val cxnIO = sql""" + |SELECT + | configuration_id, + | format, + | schema_id, + | created_at + |FROM public.issuer_credential_configuration + |WHERE issuer_id = $issuerId + """.stripMargin + .query[CredentialConfiguration] + .to[Seq] + + cxnIO + .transact(xb) + .orDie + } + + override def findCredentialConfigurationById( + issuerId: UUID, + configurationId: String + ): URIO[WalletAccessContext, Option[CredentialConfiguration]] = { + val cxnIO = sql""" + |SELECT + | configuration_id, + | format, + | schema_id, + | created_at + |FROM public.issuer_credential_configuration + |WHERE issuer_id = $issuerId AND configuration_id = $configurationId + """.stripMargin + .query[CredentialConfiguration] + .option + + cxnIO + .transactWallet(xa) + .orDie + } + + override def deleteCredentialConfiguration( + issuerId: UUID, + configurationId: String + ): URIO[WalletAccessContext, Unit] = { + val cxnIO = sql""" + | DELETE FROM public.issuer_credential_configuration + | WHERE issuer_id = $issuerId AND configuration_id = $configurationId + """.stripMargin.update + + cxnIO.run + .transactWallet(xa) + .ensureOneAffectedRowOrDie + } + +} + +object JdbcOID4VCIIssuerMetadataRepository { + val layer: URLayer[Transactor[ContextAwareTask] & Transactor[Task], OID4VCIIssuerMetadataRepository] = + ZLayer.fromFunction(new JdbcOID4VCIIssuerMetadataRepository(_, _)) +} diff --git a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataServiceSpec.scala b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataServiceSpec.scala new file mode 100644 index 0000000000..bdae719bd9 --- /dev/null +++ b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/core/service/OID4VCIIssuerMetadataServiceSpec.scala @@ -0,0 +1,33 @@ +package org.hyperledger.identus.pollux.core.service + +import org.hyperledger.identus.pollux.sql.repository.JdbcOID4VCIIssuerMetadataRepository +import org.hyperledger.identus.sharedtest.containers.PostgresTestContainerSupport +import org.hyperledger.identus.test.container.MigrationAspects +import zio.* +import zio.test.* + +object OID4VCIIssuerMetadataServiceSpec extends ZIOSpecDefault, PostgresTestContainerSupport { + + private val migration = MigrationAspects.migrateEach( + schema = "public", + paths = "classpath:sql/pollux" + ) + + private val testEnvironmentLayer = ZLayer.make[OID4VCIIssuerMetadataService]( + OID4VCIIssuerMetadataServiceImpl.layer, + JdbcOID4VCIIssuerMetadataRepository.layer, + ResourceURIDereferencerImpl.layer, + contextAwareTransactorLayer, + systemTransactorLayer + ) + + override def spec = + (suite("OID4VCIIssuerMetadataService - Jdbc repository")( + OID4VCIIssuerMetadataServiceSpecSuite.testSuite + ) @@ migration).provide( + Runtime.removeDefaultLoggers, + testEnvironmentLayer, + pgContainerLayer, + ) + +} diff --git a/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/repository/JdbcOID4VCIIssuerMetadataRepositorySpec.scala b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/repository/JdbcOID4VCIIssuerMetadataRepositorySpec.scala new file mode 100644 index 0000000000..36390023d1 --- /dev/null +++ b/pollux/sql-doobie/src/test/scala/org/hyperledger/identus/pollux/sql/repository/JdbcOID4VCIIssuerMetadataRepositorySpec.scala @@ -0,0 +1,34 @@ +package org.hyperledger.identus.pollux.sql.repository + +import org.hyperledger.identus.pollux.core.repository.{ + OID4VCIIssuerMetadataRepository, + OID4VCIIssuerMetadataRepositorySpecSuite +} +import org.hyperledger.identus.sharedtest.containers.PostgresTestContainerSupport +import org.hyperledger.identus.test.container.MigrationAspects +import zio.* +import zio.test.* + +object JdbcOID4VCIIssuerMetadataRepositorySpec extends ZIOSpecDefault, PostgresTestContainerSupport { + + private val migration = MigrationAspects.migrateEach( + schema = "public", + paths = "classpath:sql/pollux" + ) + + private val testEnvironmentLayer = ZLayer.make[OID4VCIIssuerMetadataRepository]( + JdbcOID4VCIIssuerMetadataRepository.layer, + contextAwareTransactorLayer, + systemTransactorLayer + ) + + override def spec = + (suite("JdbcOID4VCIIssuerMetadataRepository")( + OID4VCIIssuerMetadataRepositorySpecSuite.testSuite, + OID4VCIIssuerMetadataRepositorySpecSuite.multitenantTestSuite, + ) @@ migration).provide( + Runtime.removeDefaultLoggers, + testEnvironmentLayer, + pgContainerLayer, + ) +} diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala index 3e7f5bc815..6bc4494aaf 100644 --- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala +++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidJWT.scala @@ -1,12 +1,14 @@ package org.hyperledger.identus.pollux.vc.jwt import com.nimbusds.jose.{JWSAlgorithm, JWSHeader} +import com.nimbusds.jose.{JOSEObjectType, JWSAlgorithm, JWSHeader} import com.nimbusds.jose.crypto.{ECDSASigner, Ed25519Signer} import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton import com.nimbusds.jose.jwk.{Curve, ECKey} import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT} import io.circe.* import org.hyperledger.identus.shared.crypto.Ed25519KeyPair +import org.hyperledger.identus.shared.crypto.{Ed25519KeyPair, Secp256k1PrivateKey} import zio.* import java.security.* @@ -21,6 +23,19 @@ object JWT { } } +object JwtSignerImplicits { + import com.nimbusds.jose.JWSSigner + + implicit class JwtSignerProviderSecp256k1(secp256k1PrivateKey: Secp256k1PrivateKey) { + def asJwtSigner: JWSSigner = { + val ecdsaSigner = ECDSASigner(secp256k1PrivateKey.toJavaPrivateKey, Curve.SECP256K1) + val bouncyCastleProvider = BouncyCastleProviderSingleton.getInstance + ecdsaSigner.getJCAContext.setProvider(bouncyCastleProvider) + ecdsaSigner + } + } +} + trait Signer { def encode(claim: Json): JWT @@ -45,7 +60,28 @@ class ES256KSigner(privateKey: PrivateKey) extends Signer { override def encode(claim: Json): JWT = { val claimSet = JWTClaimsSet.parse(claim.noSpaces) val signedJwt = SignedJWT( - new JWSHeader.Builder(JWSAlgorithm.ES256K).build(), + new JWSHeader.Builder(JWSAlgorithm.ES256K).`type`(JOSEObjectType.JWT).build(), + claimSet + ) + signedJwt.sign(signer) + JWT(signedJwt.serialize()) + } +} + +class EdSigner(ed25519KeyPair: Ed25519KeyPair) extends Signer { + lazy val signer: Ed25519Signer = { + val ed25519Signer = Ed25519Signer(ed25519KeyPair.toOctetKeyPair) + ed25519Signer + } + + override def generateProofForJson(payload: Json, pk: PublicKey): Task[Proof] = { + EddsaJcs2022ProofGenerator.generateProof(payload, ed25519KeyPair) + } + + override def encode(claim: Json): JWT = { + val claimSet = JWTClaimsSet.parse(claim.noSpaces) + val signedJwt = SignedJWT( + new JWSHeader.Builder(JWSAlgorithm.EdDSA).build(), claimSet ) signedJwt.sign(signer) diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidResolver.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidResolver.scala index 4e5e0a53b7..99af6549ee 100644 --- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidResolver.scala +++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/DidResolver.scala @@ -177,3 +177,7 @@ class PrismDidResolver(didService: DIDService) extends DidResolver { } } + +object PrismDidResolver { + val layer: URLayer[DIDService, DidResolver] = ZLayer.fromFunction(PrismDidResolver(_)) +} diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/JWTVerification.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/JWTVerification.scala index 2ffb16eab6..9e48db7677 100644 --- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/JWTVerification.scala +++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/JWTVerification.scala @@ -9,6 +9,7 @@ import com.nimbusds.jwt.SignedJWT import io.circe import io.circe.generic.auto.* import org.hyperledger.identus.castor.core.model.did.VerificationRelationship +import org.hyperledger.identus.castor.core.model.did.{PrismDID, VerificationRelationship} import org.hyperledger.identus.shared.crypto.Ed25519PublicKey import pdi.jwt.* import zio.* @@ -85,6 +86,17 @@ object JWTVerification { loadDidDocument } + def validateIssuerFromKeyId( + extractedDID: Validation[String, String] + )(didResolver: DidResolver): IO[String, Validation[String, DIDDocument]] = { + val loadDidDocument = + ValidationUtils + .foreach(extractedDID.map(validIssuerDid => resolve(validIssuerDid)(didResolver)))(identity) + .map(b => b.flatten) + + loadDidDocument + } + def validateEncodedJwt[T](jwt: JWT, proofPurpose: Option[VerificationRelationship] = None)( didResolver: DidResolver )(decoder: String => Validation[String, T])(issuerDidExtractor: T => String): IO[String, Validation[String, Unit]] = { @@ -121,6 +133,61 @@ object JWTVerification { }) } + def keyIdDIDExtractor(jwt: JWT): Validation[String, String] = { + for { + header <- Validation + .fromTry( + JwtCirce + .decodeAll(jwt.value, JwtOptions(false, false, false)) + .map(_._1) + ) + .mapError(_.getMessage) + keyId <- Validation.fromOptionWith("Key ID not found in JWT header")(header.keyId) + isValidPrismDID <- Validation.fromEither(PrismDID.fromString(keyId)) // TODO: we don't support other DIDs yet + } yield keyId + } + + def validateEncodedJwtWithKeyId( + jwt: JWT, + proofPurpose: Option[VerificationRelationship] = None, + didResolver: DidResolver, + didExtractor: JWT => Validation[String, String] = keyIdDIDExtractor + ): IO[String, Validation[String, Unit]] = { + val decodedJWT = Validation + .fromTry(JwtCirce.decodeRawAll(jwt.value, JwtOptions(false, false, false))) + .mapError(_.getMessage) + + val extractAlgorithm: Validation[String, JwtAlgorithm] = + for { + decodedJwtTask <- decodedJWT + (header, _, _) = decodedJwtTask + algorithm <- Validation + .fromOptionWith("An algorithm must be specified in the header")(JwtCirce.parseHeader(header).algorithm) + } yield algorithm + + val claim: Validation[String, String] = + for { + decodedJwtTask <- decodedJWT + (_, claim, _) = decodedJwtTask + } yield claim + + val extractedDID = didExtractor(jwt) + + val loadDidDocument = validateIssuerFromKeyId(extractedDID)(didResolver) + + loadDidDocument + .map(validatedDidDocument => { + for { + results <- Validation.validateWith(validatedDidDocument, extractAlgorithm)((didDocument, algorithm) => + (didDocument, algorithm) + ) + (didDocument, algorithm) = results + verificationMethods <- extractVerificationMethods(didDocument, algorithm, proofPurpose) + validatedJwt <- validateEncodedJwt(jwt, verificationMethods) + } yield validatedJwt + }) + } + def toECDSAVerifier(publicKey: PublicKey): JWSVerifier = { val verifier: JWSVerifier = publicKey match { case key: ECPublicKey => ECDSAVerifier(key) diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiableCredentialPayload.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiableCredentialPayload.scala index 232661d1b0..e8682e0ac3 100644 --- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiableCredentialPayload.scala +++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/VerifiableCredentialPayload.scala @@ -19,6 +19,9 @@ import java.security.PublicKey import java.time.{Clock, Instant, OffsetDateTime, ZoneId} import java.time.temporal.TemporalAmount import scala.util.{Failure, Try} + +//TODO: I think we should remove this code and use the DID form the castor library + opaque type DID = String object DID { def apply(value: String): DID = value diff --git a/shared/core/src/main/scala/org/hyperledger/identus/shared/db/ContextAwareTask.scala b/shared/core/src/main/scala/org/hyperledger/identus/shared/db/ContextAwareTask.scala index c42fc9a684..3b07e0be97 100644 --- a/shared/core/src/main/scala/org/hyperledger/identus/shared/db/ContextAwareTask.scala +++ b/shared/core/src/main/scala/org/hyperledger/identus/shared/db/ContextAwareTask.scala @@ -13,6 +13,10 @@ import java.util.UUID trait ContextAware type ContextAwareTask[T] = Task[T] & ContextAware +object Errors { + final case class UnexpectedAffectedRow(count: Int) extends RuntimeException(s"Unexpected affected row count: $count") +} + object Implicits { given walletIdGet: Get[WalletId] = Get[UUID].map(WalletId.fromUUID) @@ -43,10 +47,10 @@ object Implicits { } - extension [Int](ma: RIO[WalletAccessContext, Int]) { + extension (ma: RIO[WalletAccessContext, Int]) { def ensureOneAffectedRowOrDie: URIO[WalletAccessContext, Unit] = ma.flatMap { case 1 => ZIO.unit - case count => ZIO.fail(RuntimeException(s"Unexpected affected row count: $count")) + case count => ZIO.fail(Errors.UnexpectedAffectedRow(count)) }.orDie }