diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index 27324c9f66..0363624932 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -26,6 +26,7 @@ jobs:
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SBT_OPTS: -Xmx2G
container:
image: ghcr.io/hyperledger-labs/ci-debian-jdk-22:0.1.0
volumes:
diff --git a/build.sbt b/build.sbt
index 3f177de778..b92face4dd 100644
--- a/build.sbt
+++ b/build.sbt
@@ -118,6 +118,7 @@ lazy val D = new {
val circeGeneric: ModuleID = "io.circe" %% "circe-generic" % V.circe
val circeParser: ModuleID = "io.circe" %% "circe-parser" % V.circe
+ val networkntJsonSchemaValidator = "com.networknt" % "json-schema-validator" % V.jsonSchemaValidator
val jwtCirce = "com.github.jwt-scala" %% "jwt-circe" % V.jwtCirceVersion
val jsonCanonicalization: ModuleID = "io.github.erdtman" % "java-json-canonicalization" % "1.1"
val titaniumJsonLd: ModuleID = "com.apicatalog" % "titanium-json-ld" % "1.4.0"
@@ -179,16 +180,28 @@ lazy val D_Shared = new {
D.zio,
D.zioHttp,
D.scalaUri,
+ D.zioPrelude,
// FIXME: split shared DB stuff as subproject?
D.doobieHikari,
D.doobiePostgres,
D.zioCatsInterop,
- D.zioPrelude,
+ )
+}
+
+lazy val D_SharedJson = new {
+ lazy val dependencies: Seq[ModuleID] =
+ Seq(
+ D.zio,
+ D.zioJson,
+ D.circeCore,
+ D.circeGeneric,
+ D.circeParser,
D.jsonCanonicalization,
D.titaniumJsonLd,
D.jakartaJson,
D.ironVC,
D.scodecBits,
+ D.networkntJsonSchemaValidator
)
}
@@ -306,8 +319,6 @@ lazy val D_Pollux_VC_JWT = new {
val zio = "dev.zio" %% "zio" % V.zio
val zioPrelude = "dev.zio" %% "zio-prelude" % V.zioPreludeVersion
- val networkntJsonSchemaValidator = "com.networknt" % "json-schema-validator" % V.jsonSchemaValidator
-
val zioTest = "dev.zio" %% "zio-test" % V.zio % Test
val zioTestSbt = "dev.zio" %% "zio-test-sbt" % V.zio % Test
val zioTestMagnolia = "dev.zio" %% "zio-test-magnolia" % V.zio % Test
@@ -315,7 +326,7 @@ lazy val D_Pollux_VC_JWT = new {
// Dependency Modules
val zioDependencies: Seq[ModuleID] = Seq(zio, zioPrelude, zioTest, zioTestSbt, zioTestMagnolia)
val baseDependencies: Seq[ModuleID] =
- zioDependencies :+ D.jwtCirce :+ networkntJsonSchemaValidator :+ D.nimbusJwt :+ D.scalaTest
+ zioDependencies :+ D.jwtCirce :+ D.networkntJsonSchemaValidator :+ D.nimbusJwt :+ D.scalaTest
// Project Dependencies
lazy val polluxVcJwtDependencies: Seq[ModuleID] = baseDependencies
@@ -456,6 +467,15 @@ lazy val shared = (project in file("shared/core"))
libraryDependencies ++= D_Shared.dependencies
)
+lazy val sharedJson = (project in file("shared/json"))
+ .settings(commonSetttings)
+ .settings(
+ name := "shared-json",
+ crossPaths := false,
+ libraryDependencies ++= D_SharedJson.dependencies
+ )
+ .dependsOn(shared)
+
lazy val sharedCrypto = (project in file("shared/crypto"))
.settings(commonSetttings)
.settings(
@@ -714,7 +734,7 @@ lazy val polluxVcJWT = project
name := "pollux-vc-jwt",
libraryDependencies ++= D_Pollux_VC_JWT.polluxVcJwtDependencies
)
- .dependsOn(castorCore)
+ .dependsOn(castorCore, sharedJson)
lazy val polluxCore = project
.in(file("pollux/core"))
@@ -734,6 +754,7 @@ lazy val polluxCore = project
polluxAnoncreds,
polluxVcJWT,
polluxSDJWT,
+ polluxPreX
)
lazy val polluxDoobie = project
@@ -747,6 +768,12 @@ lazy val polluxDoobie = project
.dependsOn(shared)
.dependsOn(sharedTest % "test->test")
+lazy val polluxPreX = project
+ .in(file("pollux/prex"))
+ .settings(commonSetttings)
+ .settings(name := "pollux-prex")
+ .dependsOn(shared, sharedJson)
+
// ########################
// ### Pollux Anoncreds ###
// ########################
@@ -891,6 +918,7 @@ releaseProcess := Seq[ReleaseStep](
lazy val aggregatedProjects: Seq[ProjectReference] = Seq(
shared,
+ sharedJson,
sharedCrypto,
sharedTest,
models,
@@ -917,6 +945,7 @@ lazy val aggregatedProjects: Seq[ProjectReference] = Seq(
polluxAnoncreds,
polluxAnoncredsTest,
polluxSDJWT,
+ polluxPreX,
connectCore,
connectDoobie,
agentWalletAPI,
diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/http/ZHttp4sBlazeServer.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/http/ZHttp4sBlazeServer.scala
index 393115be47..05d56eb62b 100644
--- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/http/ZHttp4sBlazeServer.scala
+++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/http/ZHttp4sBlazeServer.scala
@@ -9,7 +9,7 @@ import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.server.Router
import org.hyperledger.identus.api.http.ErrorResponse
import org.hyperledger.identus.shared.crypto.Sha256Hash
-import org.hyperledger.identus.shared.utils.Json
+import org.hyperledger.identus.shared.json.Json
import org.hyperledger.identus.system.controller.SystemEndpoints
import sttp.tapir.*
import sttp.tapir.model.ServerRequest
diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/codec/CirceJsonInterop.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/codec/CirceJsonInterop.scala
index d1f893316c..64269a575d 100644
--- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/codec/CirceJsonInterop.scala
+++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/api/http/codec/CirceJsonInterop.scala
@@ -1,36 +1,15 @@
package org.hyperledger.identus.api.http.codec
import io.circe.Json as CirceJson
+import org.hyperledger.identus.shared.json.JsonInterop
import sttp.tapir.json.zio.*
import sttp.tapir.Schema
import zio.json.*
import zio.json.ast.Json as ZioJson
object CirceJsonInterop {
-
- private def toZioJsonAst(circeJson: CirceJson): ZioJson = {
- val encoded = circeJson.noSpaces
- encoded.fromJson[ZioJson] match {
- case Left(failure) =>
- throw Exception(s"Circe and Zio Json interop fail. Unable to convert from Circe to Zio AST. $failure")
- case Right(value) => value
- }
- }
-
- private def toCirceJsonAst(zioJson: ZioJson): CirceJson = {
- val encoded = zioJson.toJson
- io.circe.parser.parse(encoded).left.map(_.toString) match {
- case Left(failure) =>
- throw Exception(s"Circe and Zio Json interop fail. Unable to convert from Zio to Circe AST. $failure")
- case Right(value) => value
- }
- }
-
- given encodeJson: JsonEncoder[CirceJson] = JsonEncoder[ZioJson].contramap(toZioJsonAst)
-
- given decodeJson: JsonDecoder[CirceJson] = JsonDecoder[ZioJson].map(toCirceJsonAst)
-
+ given encodeJson: JsonEncoder[CirceJson] = JsonEncoder[ZioJson].contramap(JsonInterop.toZioJsonAst)
+ given decodeJson: JsonDecoder[CirceJson] = JsonDecoder[ZioJson].map(JsonInterop.toCirceJsonAst)
given schemaJson: Schema[CirceJson] =
- Schema.derived[ZioJson].map[CirceJson](js => Some(toCirceJsonAst(js)))(toZioJsonAst)
-
+ Schema.derived[ZioJson].map[CirceJson](js => Some(JsonInterop.toCirceJsonAst(js)))(JsonInterop.toZioJsonAst)
}
diff --git a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala
index 1d215cc9fb..026e651312 100644
--- a/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala
+++ b/connect/core/src/main/scala/org/hyperledger/identus/connect/core/model/ConnectionRecord.scala
@@ -3,10 +3,7 @@ package org.hyperledger.identus.connect.core.model
import org.hyperledger.identus.connect.core.model.ConnectionRecord.{ProtocolState, Role}
import org.hyperledger.identus.mercury.protocol.connection.{ConnectionRequest, ConnectionResponse}
import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation
-import org.hyperledger.identus.shared.models.Failure
-import org.hyperledger.identus.shared.models.WalletAccessContext
-import org.hyperledger.identus.shared.models.WalletId
-import zio.ZIO
+import org.hyperledger.identus.shared.models.{Failure, WalletId}
import java.time.temporal.ChronoUnit
import java.time.Instant
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/CredentialOfferAttachment.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/CredentialOfferAttachment.scala
index ad1537f49d..9897e0f0dc 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/CredentialOfferAttachment.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/CredentialOfferAttachment.scala
@@ -2,7 +2,8 @@ package org.hyperledger.identus.pollux.core.model
import io.circe.*
import io.circe.generic.semiauto.*
-import org.hyperledger.identus.pollux.core.model.presentation.{Options, PresentationDefinition}
+import org.hyperledger.identus.pollux.core.model.presentation.Options
+import org.hyperledger.identus.pollux.prex.PresentationDefinition
final case class CredentialOfferAttachment(options: Options, presentation_definition: PresentationDefinition)
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala
index 886d44caa1..47993a370b 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/PresentationRecord.scala
@@ -4,7 +4,7 @@ import org.hyperledger.identus.mercury.model.DidId
import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation
import org.hyperledger.identus.mercury.protocol.presentproof.{Presentation, ProposePresentation, RequestPresentation}
import org.hyperledger.identus.shared.models.{Failure, WalletAccessContext, WalletId}
-import zio.{UIO, URIO, ZIO}
+import zio.{URIO, ZIO}
import java.time.temporal.ChronoUnit
import java.time.Instant
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaError.scala
index 9ced10234c..e1b1acd1f2 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaError.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/CredentialSchemaError.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.model.error
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError
+import org.hyperledger.identus.shared.json.JsonSchemaError
import org.hyperledger.identus.shared.models.{Failure, StatusCode}
sealed trait CredentialSchemaError(
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala
index 3a47821c7e..bcf096c449 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/error/PresentationError.scala
@@ -1,8 +1,8 @@
package org.hyperledger.identus.pollux.core.model.error
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError
import org.hyperledger.identus.pollux.core.model.DidCommID
import org.hyperledger.identus.pollux.core.service.URIDereferencerError
+import org.hyperledger.identus.shared.json.JsonSchemaError
import org.hyperledger.identus.shared.models.{Failure, StatusCode}
sealed trait PresentationError(
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachment.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachment.scala
index 7ee15e682b..41bd261a50 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachment.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachment.scala
@@ -2,67 +2,7 @@ package org.hyperledger.identus.pollux.core.model.presentation
import io.circe.*
import io.circe.generic.semiauto.*
-
-case class Field(
- id: Option[String] = None,
- path: Seq[String] = Seq.empty,
- name: Option[String] = None,
- purpose: Option[String] = None
-)
-object Field {
- given Encoder[Field] = deriveEncoder[Field]
- given Decoder[Field] = deriveDecoder[Field]
-}
-
-case class Jwt(alg: Seq[String], proof_type: Seq[String])
-object Jwt {
- given Encoder[Jwt] = deriveEncoder[Jwt]
- given Decoder[Jwt] = deriveDecoder[Jwt]
-}
-case class Ldp(proof_type: Seq[String])
-object Ldp {
- given Encoder[Ldp] = deriveEncoder[Ldp]
- given Decoder[Ldp] = deriveDecoder[Ldp]
-}
-case class ClaimFormat(jwt: Option[Jwt] = None, ldp: Option[Ldp] = None)
-object ClaimFormat {
- given Encoder[ClaimFormat] = deriveEncoder[ClaimFormat]
- given Decoder[ClaimFormat] = deriveDecoder[ClaimFormat]
-}
-case class Constraints(fields: Option[Seq[Field]])
-object Constraints {
- given Encoder[Constraints] = deriveEncoder[Constraints]
- given Decoder[Constraints] = deriveDecoder[Constraints]
-}
-
-/** Refer to Input Descriptors
- */
-case class InputDescriptor(
- id: String = java.util.UUID.randomUUID.toString(),
- name: Option[String] = None,
- purpose: Option[String] = None,
- format: Option[ClaimFormat] = None,
- constraints: Constraints
-)
-object InputDescriptor {
- given Encoder[InputDescriptor] = deriveEncoder[InputDescriptor]
- given Decoder[InputDescriptor] = deriveDecoder[InputDescriptor]
-}
-
-/** Refer to Presentation
- * Definition
- */
-case class PresentationDefinition(
- id: String = java.util.UUID.randomUUID.toString(), // UUID
- input_descriptors: Seq[InputDescriptor] = Seq.empty,
- name: Option[String] = None,
- purpose: Option[String] = None,
- format: Option[ClaimFormat] = None
-)
-object PresentationDefinition {
- given Encoder[PresentationDefinition] = deriveEncoder[PresentationDefinition]
- given Decoder[PresentationDefinition] = deriveDecoder[PresentationDefinition]
-}
+import org.hyperledger.identus.pollux.prex.PresentationDefinition
case class Options(challenge: String, domain: String)
object Options {
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 2486c17cf2..809f0b1c88 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
@@ -8,8 +8,8 @@ import org.hyperledger.identus.pollux.core.model.schema.`type`.{
CredentialSchemaType
}
import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
-import org.hyperledger.identus.pollux.core.model.schema.validator.{JsonSchemaValidator, JsonSchemaValidatorImpl}
import org.hyperledger.identus.pollux.core.service.URIDereferencer
+import org.hyperledger.identus.shared.json.{JsonSchemaValidator, JsonSchemaValidatorImpl}
import zio.*
import zio.json.*
import zio.json.ast.Json
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/AnoncredSchemaType.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/AnoncredSchemaType.scala
index b8a699d957..7a5ae12ca2 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/AnoncredSchemaType.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/AnoncredSchemaType.scala
@@ -3,13 +3,8 @@ package org.hyperledger.identus.pollux.core.model.schema.`type`
import com.networknt.schema.*
import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1.*
-import org.hyperledger.identus.pollux.core.model.schema.validator.{
- JsonSchemaError,
- JsonSchemaUtils,
- JsonSchemaValidatorImpl,
- SchemaSerDes
-}
import org.hyperledger.identus.pollux.core.model.schema.Schema
+import org.hyperledger.identus.shared.json.{JsonSchemaError, JsonSchemaUtils, JsonSchemaValidatorImpl, SchemaSerDes}
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaSerDesV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaSerDesV1.scala
index e0659beae9..d3cf6336d9 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaSerDesV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaSerDesV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.model.schema.`type`
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
import zio.json.ast.Json
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaType.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaType.scala
index 0efa26528c..8e1a4b2e52 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaType.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialJsonSchemaType.scala
@@ -1,7 +1,7 @@
package org.hyperledger.identus.pollux.core.model.schema.`type`
-import org.hyperledger.identus.pollux.core.model.schema.validator.{JsonSchemaError, JsonSchemaValidatorImpl}
import org.hyperledger.identus.pollux.core.model.schema.Schema
+import org.hyperledger.identus.shared.json.{JsonSchemaError, JsonSchemaValidatorImpl}
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialSchemaType.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialSchemaType.scala
index 5dee559af8..03edd43936 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialSchemaType.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/CredentialSchemaType.scala
@@ -1,7 +1,7 @@
package org.hyperledger.identus.pollux.core.model.schema.`type`
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError
import org.hyperledger.identus.pollux.core.model.schema.Schema
+import org.hyperledger.identus.shared.json.JsonSchemaError
import zio.IO
trait CredentialSchemaType {
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/anoncred/AnoncredSchemaSerDesV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/anoncred/AnoncredSchemaSerDesV1.scala
index 3890974da2..5e0ae8f9e4 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/anoncred/AnoncredSchemaSerDesV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/type/anoncred/AnoncredSchemaSerDesV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaError.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaError.scala
deleted file mode 100644
index 73993f1959..0000000000
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaError.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.hyperledger.identus.pollux.core.model.schema.validator
-
-sealed trait JsonSchemaError {
- def error: String
-}
-
-object JsonSchemaError {
- case class JsonSchemaParsingError(error: String) extends JsonSchemaError
-
- case class JsonValidationErrors(errors: Seq[String]) extends JsonSchemaError {
- def error: String = errors.mkString(";")
- }
-
- case class UnsupportedJsonSchemaSpecVersion(error: String) extends JsonSchemaError
-
- case class UnexpectedError(error: String) extends JsonSchemaError
-}
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaUtils.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaUtils.scala
deleted file mode 100644
index db76a7439a..0000000000
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaUtils.scala
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.hyperledger.identus.pollux.core.model.schema.validator
-
-import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
-import com.networknt.schema.*
-import com.networknt.schema.SpecVersion.VersionFlag
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError.*
-import zio.*
-import zio.json.ast.Json
-
-object JsonSchemaUtils {
- def jsonSchema(
- schema: String,
- supportedVersions: IndexedSeq[VersionFlag] = IndexedSeq.empty
- ): IO[JsonSchemaError, JsonSchema] = {
- for {
- jsonSchemaNode <- toJsonNode(schema)
- specVersion <- ZIO
- .attempt(SpecVersionDetector.detect(jsonSchemaNode))
- .mapError(t => UnexpectedError(t.getMessage))
- _ <-
- if (supportedVersions.nonEmpty && !supportedVersions.contains(specVersion))
- ZIO.fail(
- UnsupportedJsonSchemaSpecVersion(
- s"Unsupported JsonSchemaVersion. Current:$specVersion ExpectedOneOf:${supportedVersions.map(_.getId)}"
- )
- )
- else ZIO.unit
- mapper <- ZIO.attempt(new ObjectMapper()).mapError(t => UnexpectedError(t.getMessage))
- factory <- ZIO
- .attempt(JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(specVersion)).jsonMapper(mapper).build)
- .mapError(t => UnexpectedError(t.getMessage))
- jsonSchema <- ZIO.attempt(factory.getSchema(jsonSchemaNode)).mapError(t => UnexpectedError(t.getMessage))
- } yield jsonSchema
- }
-
- def from(
- schema: Json,
- supportedVersions: IndexedSeq[VersionFlag] = IndexedSeq.empty
- ): IO[JsonSchemaError, JsonSchema] = {
- jsonSchema(schema.toString(), supportedVersions)
- }
-
- def toJsonNode(json: Json): IO[JsonSchemaError, JsonNode] = {
- toJsonNode(json.toString())
- }
-
- def toJsonNode(json: String): IO[JsonSchemaError, JsonNode] = {
- for {
- mapper <- ZIO.attempt(new ObjectMapper()).mapError(t => UnexpectedError(t.getMessage))
- jsonSchemaNode <- ZIO
- .attempt(mapper.readTree(json))
- .mapError(t => JsonSchemaParsingError(t.getMessage))
- } yield jsonSchemaNode
- }
-}
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaValidator.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaValidator.scala
deleted file mode 100644
index 31e002323e..0000000000
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaValidator.scala
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.hyperledger.identus.pollux.core.model.schema.validator
-
-import com.fasterxml.jackson.databind.JsonNode
-import zio.*
-
-trait JsonSchemaValidator {
- def validate(claims: String): IO[JsonSchemaError, Unit]
-
- def validate(claimsJsonNode: JsonNode): IO[JsonSchemaError, Unit] = {
- validate(claimsJsonNode.toString)
- }
-}
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaValidatorImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaValidatorImpl.scala
deleted file mode 100644
index 7ad58c51b1..0000000000
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/JsonSchemaValidatorImpl.scala
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.hyperledger.identus.pollux.core.model.schema.validator
-
-import com.networknt.schema.*
-import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError
-import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.*
-import org.hyperledger.identus.pollux.core.model.schema.Schema
-import zio.*
-
-case class JsonSchemaValidatorImpl(schemaValidator: JsonSchema) extends JsonSchemaValidator {
- override def validate(jsonString: String): IO[JsonSchemaError, Unit] = {
- import scala.jdk.CollectionConverters.*
- for {
- // Convert claims to JsonNode
- jsonClaims <- JsonSchemaUtils.toJsonNode(jsonString)
-
- // Validate claims JsonNode
- validationMessages <- ZIO
- .attempt(schemaValidator.validate(jsonClaims).asScala.toSeq)
- .mapError(t => JsonSchemaError.JsonValidationErrors(Seq(t.getMessage)))
-
- validationResult <-
- if (validationMessages.isEmpty) ZIO.unit
- else ZIO.fail(JsonSchemaError.JsonValidationErrors(validationMessages.map(_.getMessage)))
- } yield validationResult
- }
-
-}
-
-object JsonSchemaValidatorImpl {
- def from(schema: Schema): IO[JsonSchemaError, JsonSchemaValidatorImpl] = {
- for {
- jsonSchema <- JsonSchemaUtils.from(schema, IndexedSeq(SpecVersion.VersionFlag.V202012))
- } yield JsonSchemaValidatorImpl(jsonSchema)
- }
-}
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/SchemaSerDes.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/SchemaSerDes.scala
deleted file mode 100644
index 982a91ff8e..0000000000
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/model/schema/validator/SchemaSerDes.scala
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.hyperledger.identus.pollux.core.model.schema.validator
-
-import com.networknt.schema.JsonSchema
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError.*
-import zio.{IO, ZIO}
-import zio.json.*
-import zio.json.ast.Json
-import zio.json.ast.Json.*
-
-class SchemaSerDes[S](jsonSchemaSchemaStr: String) {
-
- def initialiseJsonSchema: IO[JsonSchemaError, JsonSchema] =
- JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr)
-
- def serializeToJsonString(instance: S)(using encoder: JsonEncoder[S]): String = {
- instance.toJson
- }
-
- def serialize(instance: S)(using encoder: JsonEncoder[S]): Either[String, Json] = {
- instance.toJsonAST
- }
-
- def deserialize(
- schema: zio.json.ast.Json
- )(using decoder: JsonDecoder[S]): IO[JsonSchemaError, S] = {
- deserialize(schema.toString())
- }
-
- def deserialize(
- jsonString: String
- )(using decoder: JsonDecoder[S]): IO[JsonSchemaError, S] = {
- for {
- _ <- validate(jsonString)
- anoncredSchema <-
- ZIO
- .fromEither(decoder.decodeJson(jsonString))
- .mapError(JsonSchemaError.JsonSchemaParsingError.apply)
- } yield anoncredSchema
- }
-
- def deserializeAsJson(jsonString: String): IO[JsonSchemaError, Json] = {
- for {
- _ <- validate(jsonString)
- json <-
- ZIO
- .fromEither(jsonString.fromJson[Json])
- .mapError(JsonSchemaError.JsonSchemaParsingError.apply)
- } yield json
- }
-
- def validate(jsonString: String): IO[JsonSchemaError, Unit] = {
- for {
- jsonSchemaSchema <- JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr)
- schemaValidator = JsonSchemaValidatorImpl(jsonSchemaSchema)
- result <- schemaValidator.validate(jsonString)
- } yield result
- }
-
-}
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala
index 9dcca15cee..68e76e8f22 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialDefinitionServiceImpl.scala
@@ -16,7 +16,6 @@ import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.{
InvalidURI
}
import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError
import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition
import org.hyperledger.identus.pollux.core.model.schema.CredentialDefinition.{Filter, FilteredEntries}
import org.hyperledger.identus.pollux.core.model.secret.CredentialDefinitionSecret
@@ -27,6 +26,7 @@ import org.hyperledger.identus.pollux.core.service.serdes.{
ProofKeyCredentialDefinitionSchemaSerDesV1,
PublicCredentialDefinitionSerDesV1
}
+import org.hyperledger.identus.shared.json.JsonSchemaError
import zio.*
import java.net.URI
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 8ace946882..cd4d35ffbc 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
@@ -24,6 +24,7 @@ import org.hyperledger.identus.pollux.core.model.secret.CredentialDefinitionSecr
import org.hyperledger.identus.pollux.core.model.CredentialFormat.AnonCreds
import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.ProtocolState.OfferReceived
import org.hyperledger.identus.pollux.core.repository.{CredentialRepository, CredentialStatusListRepository}
+import org.hyperledger.identus.pollux.prex.{ClaimFormat, Jwt, PresentationDefinition}
import org.hyperledger.identus.pollux.sdjwt.*
import org.hyperledger.identus.pollux.vc.jwt.{Issuer as JwtIssuer, *}
import org.hyperledger.identus.shared.crypto.{Ed25519KeyPair, Ed25519PublicKey, Secp256k1KeyPair}
@@ -967,7 +968,7 @@ class CredentialServiceImpl(
format = Some(offerFormat.name),
payload = PresentationAttachment(
Some(Options(challenge, domain)),
- PresentationDefinition(format = Some(ClaimFormat(jwt = Some(Jwt(alg = Seq("ES256K"), proof_type = Nil)))))
+ PresentationDefinition(format = Some(ClaimFormat(jwt = Some(Jwt(alg = Seq("ES256K"))))))
)
)
)
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala
index cb7d435905..ce4b3ab333 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/PresentationServiceNotifier.scala
@@ -17,7 +17,7 @@ import org.hyperledger.identus.pollux.core.service.serdes.{AnoncredCredentialPro
import org.hyperledger.identus.pollux.sdjwt.{HolderPrivateKey, PresentationCompact}
import org.hyperledger.identus.pollux.vc.jwt.{Issuer, PresentationPayload, W3cCredentialPayload}
import org.hyperledger.identus.shared.models.*
-import zio._
+import zio.*
import zio.json.*
import java.time.Instant
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala
index a28807b781..efc87e761c 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredCredentialProofsV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.service.serdes
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala
index 18edcfe9e0..9092edf3c3 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationRequestV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.service.serdes
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationV1.scala
index ccca4fa58c..1b149ad247 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/AnoncredPresentationV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.service.serdes
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PrivateCredentialDefinitionSchemaSerDesV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PrivateCredentialDefinitionSchemaSerDesV1.scala
index 5bea91b7f2..03f085b1f2 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PrivateCredentialDefinitionSchemaSerDesV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PrivateCredentialDefinitionSchemaSerDesV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.service.serdes
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/ProofKeyCredentialDefinitionSchemaSerDesV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/ProofKeyCredentialDefinitionSchemaSerDesV1.scala
index ee15cf065e..2c0f03b965 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/ProofKeyCredentialDefinitionSchemaSerDesV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/ProofKeyCredentialDefinitionSchemaSerDesV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.service.serdes
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesV1.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesV1.scala
index af6e84ae26..ef238f4c8c 100644
--- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesV1.scala
+++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/serdes/PublicCredentialDefinitionSchemaSerDesV1.scala
@@ -1,6 +1,6 @@
package org.hyperledger.identus.pollux.core.service.serdes
-import org.hyperledger.identus.pollux.core.model.schema.validator.SchemaSerDes
+import org.hyperledger.identus.shared.json.SchemaSerDes
import zio.*
import zio.json.*
diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachmentSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachmentSpec.scala
index 52109d1a8c..b4e77143e7 100644
--- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachmentSpec.scala
+++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/presentation/PresentationAttachmentSpec.scala
@@ -4,6 +4,7 @@ import io.circe.parser.*
import io.circe.syntax.*
import io.circe.Json
import munit.*
+import org.hyperledger.identus.pollux.prex.*
class PresentationAttachmentSpec extends ZSuite {
diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/AnoncredSchemaTypeSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/AnoncredSchemaTypeSpec.scala
index 6d8d3fd686..9b3be99fbd 100644
--- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/AnoncredSchemaTypeSpec.scala
+++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/AnoncredSchemaTypeSpec.scala
@@ -1,7 +1,7 @@
package org.hyperledger.identus.pollux.core.model.schema
import org.hyperledger.identus.pollux.core.model.schema.`type`.AnoncredSchemaType
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError
+import org.hyperledger.identus.shared.json.JsonSchemaError
import zio.*
import zio.json.*
import zio.json.ast.Json
diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala
index 0d10160e21..341565de22 100644
--- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala
+++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/model/schema/CredentialSchemaSpec.scala
@@ -4,8 +4,8 @@ import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError
import org.hyperledger.identus.pollux.core.model.error.CredentialSchemaError.CredentialSchemaValidationError
import org.hyperledger.identus.pollux.core.model.schema.`type`.{AnoncredSchemaType, CredentialJsonSchemaType}
import org.hyperledger.identus.pollux.core.model.schema.`type`.anoncred.AnoncredSchemaSerDesV1
-import org.hyperledger.identus.pollux.core.model.schema.validator.JsonSchemaError.JsonValidationErrors
import org.hyperledger.identus.pollux.core.model.schema.AnoncredSchemaTypeSpec.test
+import org.hyperledger.identus.shared.json.JsonSchemaError.JsonValidationErrors
import zio.json.*
import zio.json.ast.Json
import zio.json.ast.Json.*
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 8eb43c7353..e8d9fcf907 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
@@ -9,12 +9,13 @@ import org.hyperledger.identus.castor.core.service.DIDService
import org.hyperledger.identus.mercury.model.{AttachmentDescriptor, DidId}
import org.hyperledger.identus.mercury.protocol.issuecredential.*
import org.hyperledger.identus.pollux.core.model.*
-import org.hyperledger.identus.pollux.core.model.presentation.{ClaimFormat, Ldp, Options, PresentationDefinition}
+import org.hyperledger.identus.pollux.core.model.presentation.Options
import org.hyperledger.identus.pollux.core.repository.{
CredentialDefinitionRepositoryInMemory,
CredentialRepositoryInMemory,
CredentialStatusListRepositoryInMemory
}
+import org.hyperledger.identus.pollux.prex.{ClaimFormat, Ldp, PresentationDefinition}
import org.hyperledger.identus.pollux.vc.jwt.*
import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId}
import zio.*
diff --git a/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationDefinition.scala b/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationDefinition.scala
new file mode 100644
index 0000000000..5789c214c0
--- /dev/null
+++ b/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationDefinition.scala
@@ -0,0 +1,100 @@
+package org.hyperledger.identus.pollux.prex
+
+import com.networknt.schema.{JsonSchema, SpecVersion}
+import io.circe.*
+import io.circe.generic.semiauto.*
+import io.circe.Json as CirceJson
+import org.hyperledger.identus.shared.json.{JsonInterop, JsonSchemaError, JsonSchemaUtils}
+import zio.*
+import zio.json.ast.Json as ZioJson
+
+// TODO: define proper type
+type JsonPath = String
+
+opaque type FieldFilter = ZioJson
+
+object FieldFilter {
+ given Encoder[FieldFilter] = Encoder.encodeJson.contramap(JsonInterop.toCirceJsonAst)
+ given Decoder[FieldFilter] = Decoder.decodeJson.map(JsonInterop.toZioJsonAst)
+
+ extension (f: FieldFilter)
+ def asJsonZio: ZioJson = f
+ def asJsonCirce: CirceJson = JsonInterop.toCirceJsonAst(f)
+
+ // Json schema draft 7 must be used
+ // https://identity.foundation/presentation-exchange/spec/v2.1.1/#json-schema
+ def toJsonSchema: IO[JsonSchemaError, JsonSchema] =
+ JsonSchemaUtils.jsonSchemaAtVersion(f.toString(), SpecVersion.VersionFlag.V7)
+}
+
+case class Field(
+ id: Option[String] = None,
+ path: Seq[JsonPath] = Seq.empty,
+ name: Option[String] = None,
+ purpose: Option[String] = None,
+ filter: Option[FieldFilter] = None
+)
+
+object Field {
+ given Encoder[Field] = deriveEncoder[Field]
+ given Decoder[Field] = deriveDecoder[Field]
+}
+
+case class Jwt(alg: Seq[String])
+
+object Jwt {
+ given Encoder[Jwt] = deriveEncoder[Jwt]
+ given Decoder[Jwt] = deriveDecoder[Jwt]
+}
+
+case class Ldp(proof_type: Seq[String])
+
+object Ldp {
+ given Encoder[Ldp] = deriveEncoder[Ldp]
+ given Decoder[Ldp] = deriveDecoder[Ldp]
+}
+
+case class ClaimFormat(jwt: Option[Jwt] = None, ldp: Option[Ldp] = None)
+
+object ClaimFormat {
+ given Encoder[ClaimFormat] = deriveEncoder[ClaimFormat]
+ given Decoder[ClaimFormat] = deriveDecoder[ClaimFormat]
+}
+
+case class Constraints(fields: Option[Seq[Field]])
+
+object Constraints {
+ given Encoder[Constraints] = deriveEncoder[Constraints]
+ given Decoder[Constraints] = deriveDecoder[Constraints]
+}
+
+/** Refer to Input Descriptors
+ */
+case class InputDescriptor(
+ id: String = java.util.UUID.randomUUID.toString(),
+ name: Option[String] = None,
+ purpose: Option[String] = None,
+ format: Option[ClaimFormat] = None,
+ constraints: Constraints
+)
+
+object InputDescriptor {
+ given Encoder[InputDescriptor] = deriveEncoder[InputDescriptor]
+ given Decoder[InputDescriptor] = deriveDecoder[InputDescriptor]
+}
+
+/** Refer to Presentation
+ * Definition
+ */
+case class PresentationDefinition(
+ id: String = java.util.UUID.randomUUID.toString(), // UUID
+ input_descriptors: Seq[InputDescriptor] = Seq.empty,
+ name: Option[String] = None,
+ purpose: Option[String] = None,
+ format: Option[ClaimFormat] = None
+)
+
+object PresentationDefinition {
+ given Encoder[PresentationDefinition] = deriveEncoder[PresentationDefinition]
+ given Decoder[PresentationDefinition] = deriveDecoder[PresentationDefinition]
+}
diff --git a/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationDefinitionValidator.scala b/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationDefinitionValidator.scala
new file mode 100644
index 0000000000..3160d373dc
--- /dev/null
+++ b/pollux/prex/src/main/scala/org/hyperledger/identus/pollux/prex/PresentationDefinitionValidator.scala
@@ -0,0 +1,88 @@
+package org.hyperledger.identus.pollux.prex
+
+import org.hyperledger.identus.pollux.prex.PresentationDefinitionError.{
+ InvalidFilterJsonSchema,
+ JsonSchemaOptionNotSupported
+}
+import org.hyperledger.identus.shared.json.{JsonSchemaError, JsonSchemaValidator, JsonSchemaValidatorImpl}
+import org.hyperledger.identus.shared.models.{Failure, StatusCode}
+import zio.*
+
+import scala.jdk.CollectionConverters.*
+
+sealed trait PresentationDefinitionError extends Failure {
+ override def namespace: String = "PresentationDefinitionError"
+}
+
+object PresentationDefinitionError {
+ final case class InvalidFilterJsonSchema(json: String, error: JsonSchemaError) extends PresentationDefinitionError {
+ override def statusCode: StatusCode = StatusCode.BadRequest
+ override def userFacingMessage: String =
+ s"PresentationDefinition input_descriptors filter '$json' is not a valid JsonSchema Draft 7"
+ }
+
+ final case class JsonSchemaOptionNotSupported(invalidKeys: Set[String], allowedKeys: Set[String])
+ extends PresentationDefinitionError {
+ override def statusCode: StatusCode = StatusCode.BadRequest
+ override def userFacingMessage: String = {
+ val invalidKeysStr = invalidKeys.mkString(", ")
+ val allowedKeysStr = allowedKeys.mkString(", ")
+ s"PresentationDefinition input_descriptors filter json schema contains unsupported keys: $invalidKeysStr. Supported keys are: $allowedKeysStr"
+ }
+ }
+}
+
+trait PresentationDefinitionValidator {
+ def validate(pd: PresentationDefinition): IO[PresentationDefinitionError, Unit]
+}
+
+object PresentationDefinitionValidatorImpl {
+ def layer: Layer[JsonSchemaError, PresentationDefinitionValidator] =
+ ZLayer.scoped {
+ JsonSchemaValidatorImpl.draft7Meta
+ .map(PresentationDefinitionValidatorImpl(_))
+ }
+}
+
+class PresentationDefinitionValidatorImpl(filterSchemaValidator: JsonSchemaValidator)
+ extends PresentationDefinitionValidator {
+ override def validate(pd: PresentationDefinition): IO[PresentationDefinitionError, Unit] = {
+ val filters = pd.input_descriptors
+ .flatMap(_.constraints.fields)
+ .flatten
+ .flatMap(_.filter)
+
+ for {
+ _ <- validateFilters(filters)
+ _ <- validateAllowedFilterSchemaKeys(filters)
+ } yield ()
+ }
+
+ // while we use full-blown json-schema library, we limit the schema optiton
+ // to make sure verfier don't go crazy on schema causing problem with holder interoperability
+ // see SDK supported keys https://github.com/hyperledger/identus-edge-agent-sdk-ts/blob/da27890ad4ff3d32576bda8bc99a1185e7239a4c/src/domain/models/VerifiableCredential.ts#L120
+ private def validateAllowedFilterSchemaKeys(filters: Seq[FieldFilter]): IO[PresentationDefinitionError, Unit] = {
+ val allowedSchemaKeys = Set("type", "pattern", "enum", "const", "value")
+ ZIO
+ .foreach(filters) { filter =>
+ val schemaKeys = filter.asJsonZio.asObject.fold(Seq.empty)(_.keys.toSeq)
+ val invalidKeys = schemaKeys.filterNot(allowedSchemaKeys.contains)
+
+ if invalidKeys.isEmpty
+ then ZIO.unit
+ else ZIO.fail(JsonSchemaOptionNotSupported(invalidKeys.toSet, allowedSchemaKeys))
+ }
+ .unit
+ }
+
+ private def validateFilters(filters: Seq[FieldFilter]): IO[PresentationDefinitionError, Unit] =
+ ZIO
+ .foreach(filters) { filter =>
+ val json = filter.asJsonZio.toString()
+ filterSchemaValidator
+ .validate(json)
+ .mapError(InvalidFilterJsonSchema(json, _))
+ }
+ .unit
+
+}
diff --git a/pollux/prex/src/test/resources/pd/filter_by_cred_type.json b/pollux/prex/src/test/resources/pd/filter_by_cred_type.json
new file mode 100644
index 0000000000..bb7fee3ea0
--- /dev/null
+++ b/pollux/prex/src/test/resources/pd/filter_by_cred_type.json
@@ -0,0 +1,22 @@
+{
+ "presentation_definition": {
+ "id": "first simple example",
+ "input_descriptors": [
+ {
+ "id": "A specific type of VC",
+ "name": "A specific type of VC",
+ "purpose": "We want a VC of this type",
+ "constraints": {
+ "fields": [
+ {
+ "path": ["$.type"],
+ "filter": {
+ "type": "array"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/pollux/prex/src/test/resources/pd/minimal_example.json b/pollux/prex/src/test/resources/pd/minimal_example.json
new file mode 100644
index 0000000000..801d2a99cb
--- /dev/null
+++ b/pollux/prex/src/test/resources/pd/minimal_example.json
@@ -0,0 +1,25 @@
+{
+ "comment": "Note: VP, OIDC, DIDComm, or CHAPI outer wrapper would be here.",
+ "presentation_definition": {
+ "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
+ "input_descriptors": [
+ {
+ "id": "wa_driver_license",
+ "name": "Washington State Business License",
+ "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
+ "constraints": {
+ "fields": [
+ {
+ "path": [
+ "$.credentialSubject.dateOfBirth",
+ "$.credentialSubject.dob",
+ "$.vc.credentialSubject.dateOfBirth",
+ "$.vc.credentialSubject.dob"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/pollux/prex/src/test/resources/pd/single_group.json b/pollux/prex/src/test/resources/pd/single_group.json
new file mode 100644
index 0000000000..3a140f3650
--- /dev/null
+++ b/pollux/prex/src/test/resources/pd/single_group.json
@@ -0,0 +1,76 @@
+{
+ "comment": "VP, OIDC, DIDComm, or CHAPI outer wrapper here",
+ "presentation_definition": {
+ "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
+ "submission_requirements": [
+ {
+ "name": "Citizenship Information",
+ "rule": "pick",
+ "count": 1,
+ "from": "A"
+ }
+ ],
+ "input_descriptors": [
+ {
+ "id": "citizenship_input_1",
+ "name": "EU Driver's License",
+ "group": ["A"],
+ "constraints": {
+ "fields": [
+ {
+ "path": ["$.credentialSchema.id", "$.vc.credentialSchema.id"],
+ "filter": {
+ "type": "string",
+ "const": "https://eu.com/claims/DriversLicense.json"
+ }
+ },
+ {
+ "path": ["$.issuer", "$.vc.issuer", "$.iss"],
+ "purpose": "We can only accept digital driver's licenses issued by national authorities of member states or trusted notarial auditors.",
+ "filter": {
+ "type": "string",
+ "pattern": "^did:example:gov1$|^did:example:gov2$"
+ }
+ },
+ {
+ "path": [
+ "$.credentialSubject.dob",
+ "$.vc.credentialSubject.dob",
+ "$.dob"
+ ],
+ "filter": {
+ "type": "string"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "id": "citizenship_input_2",
+ "name": "US Passport",
+ "group": ["A"],
+ "constraints": {
+ "fields": [
+ {
+ "path": ["$.credentialSchema.id", "$.vc.credentialSchema.id"],
+ "filter": {
+ "type": "string",
+ "const": "hub://did:foo:123/Collections/schema.us.gov/passport.json"
+ }
+ },
+ {
+ "path": [
+ "$.credentialSubject.birth_date",
+ "$.vc.credentialSubject.birth_date",
+ "$.birth_date"
+ ],
+ "filter": {
+ "type": "string"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/pollux/prex/src/test/resources/pd/two_filters_simplified.json b/pollux/prex/src/test/resources/pd/two_filters_simplified.json
new file mode 100644
index 0000000000..5c6b24a768
--- /dev/null
+++ b/pollux/prex/src/test/resources/pd/two_filters_simplified.json
@@ -0,0 +1,37 @@
+{
+ "presentation_definition": {
+ "id": "Scalable trust example",
+ "input_descriptors": [
+ {
+ "id": "any type of credit card from any bank",
+ "name": "any type of credit card from any bank",
+ "purpose": "Please provide your credit card details",
+ "constraints": {
+ "fields": [
+ {
+ "path": ["$.termsOfUse.type"],
+ "filter": {
+ "type": "string",
+ "pattern": "^https://train.trust-scheme.de/info$"
+ }
+ },
+ {
+ "path": ["$.termsOfUse.trustScheme"],
+ "filter": {
+ "type": "string",
+ "pattern": "^worldbankfederation.com$"
+ }
+ },
+ {
+ "path": ["$.type"],
+ "filter": {
+ "type": "string",
+ "pattern": "^creditCard$"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
diff --git a/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationDefinitionValidatorSpec.scala b/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationDefinitionValidatorSpec.scala
new file mode 100644
index 0000000000..bd57c15230
--- /dev/null
+++ b/pollux/prex/src/test/scala/org/hyperledger/identus/pollux/prex/PresentationDefinitionValidatorSpec.scala
@@ -0,0 +1,115 @@
+package org.hyperledger.identus.pollux.prex
+
+import io.circe.*
+import io.circe.generic.auto.*
+import io.circe.parser.*
+import org.hyperledger.identus.pollux.prex.PresentationDefinitionError.{
+ InvalidFilterJsonSchema,
+ JsonSchemaOptionNotSupported
+}
+import zio.*
+import zio.test.*
+import zio.test.Assertion.*
+
+import scala.io.Source
+import scala.util.Using
+
+object PresentationDefinitionValidatorSpec extends ZIOSpecDefault {
+
+ final case class ExampleTransportEnvelope(presentation_definition: PresentationDefinition)
+
+ override def spec = suite("PresentationDefinitionValidatorSpec")(
+ test("accept presentation-definition examples from spec") {
+ val resourcePaths = Seq(
+ "pd/minimal_example.json",
+ "pd/filter_by_cred_type.json",
+ "pd/two_filters_simplified.json",
+ "pd/single_group.json",
+ )
+ ZIO
+ .foreach(resourcePaths) { path =>
+ for {
+ validator <- ZIO.service[PresentationDefinitionValidator]
+ _ <- ZIO
+ .fromTry(Using(Source.fromResource(path))(_.mkString))
+ .flatMap(json => ZIO.fromEither(decode[ExampleTransportEnvelope](json)))
+ .map(_.presentation_definition)
+ .flatMap(validator.validate)
+ } yield ()
+ }
+ .as(assertCompletes)
+ },
+ test("reject when filter is invalid json-schema") {
+ val pdJson =
+ """{
+ | "presentation_definition": {
+ | "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
+ | "input_descriptors": [
+ | {
+ | "id": "wa_driver_license",
+ | "name": "Washington State Business License",
+ | "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
+ | "constraints": {
+ | "fields": [
+ | {
+ | "path": ["$.credentialSubject.dateOfBirth"],
+ | "filter": {"type": 123}
+ | }
+ | ]
+ | }
+ | }
+ | ]
+ | }
+ |}
+ """.stripMargin
+
+ for {
+ validator <- ZIO.service[PresentationDefinitionValidator]
+ pd <- ZIO
+ .fromEither(decode[ExampleTransportEnvelope](pdJson))
+ .map(_.presentation_definition)
+ filters <- ZIO
+ .succeed(pd.input_descriptors.flatMap(_.constraints.fields.getOrElse(Nil)))
+ .map(_.flatMap(_.filter))
+ exit <- validator.validate(pd).exit
+ } yield assert(exit)(failsWithA[InvalidFilterJsonSchema])
+ },
+ test("reject when filter is valid but use disallowed filter property") {
+ val pdJson =
+ """{
+ | "presentation_definition": {
+ | "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
+ | "input_descriptors": [
+ | {
+ | "id": "wa_driver_license",
+ | "name": "Washington State Business License",
+ | "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
+ | "constraints": {
+ | "fields": [
+ | {
+ | "path": ["$.credentialSubject.dateOfBirth"],
+ | "filter": {"type": "string", "format": "date-time"}
+ | }
+ | ]
+ | }
+ | }
+ | ]
+ | }
+ |}
+ """.stripMargin
+
+ for {
+ validator <- ZIO.service[PresentationDefinitionValidator]
+ pd <- ZIO
+ .fromEither(decode[ExampleTransportEnvelope](pdJson))
+ .map(_.presentation_definition)
+ filters <- ZIO
+ .succeed(pd.input_descriptors.flatMap(_.constraints.fields.getOrElse(Nil)))
+ .map(_.flatMap(_.filter))
+ exit <- validator.validate(pd).exit
+ } yield assert(exit)(failsWithA[JsonSchemaOptionNotSupported])
+ }
+ )
+ .provide(PresentationDefinitionValidatorImpl.layer)
+
+}
diff --git a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/Proof.scala b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/Proof.scala
index 8bf6ab2086..ca27945182 100644
--- a/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/Proof.scala
+++ b/pollux/vc-jwt/src/main/scala/org/hyperledger/identus/pollux/vc/jwt/Proof.scala
@@ -7,7 +7,8 @@ import com.nimbusds.jwt.SignedJWT
import io.circe.*
import io.circe.syntax.*
import org.hyperledger.identus.shared.crypto.{Ed25519KeyPair, Ed25519PublicKey, KmpEd25519KeyOps}
-import org.hyperledger.identus.shared.utils.{Base64Utils, Json as JsonUtils}
+import org.hyperledger.identus.shared.json.Json as JsonUtils
+import org.hyperledger.identus.shared.utils.Base64Utils
import scodec.bits.ByteVector
import zio.*
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 9a670fbb6a..64ebc7902d 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
@@ -8,7 +8,7 @@ import io.circe.parser.decode
import io.circe.syntax.*
import org.hyperledger.identus.castor.core.model.did.VerificationRelationship
import org.hyperledger.identus.pollux.vc.jwt.revocation.BitString
-import org.hyperledger.identus.shared.crypto.{KmpSecp256k1KeyOps, PublicKey as ApolloPublicKey}
+import org.hyperledger.identus.shared.crypto.KmpSecp256k1KeyOps
import org.hyperledger.identus.shared.http.UriResolver
import org.hyperledger.identus.shared.utils.Base64Utils
import pdi.jwt.*
diff --git a/project/LicenseReport.scala b/project/LicenseReport.scala
index 470172628b..403fa77d96 100644
--- a/project/LicenseReport.scala
+++ b/project/LicenseReport.scala
@@ -1,7 +1,7 @@
-import sbt.Logger
+import sbt.internal.librarymanagement.{IvyRetrieve, IvySbt}
import sbt.librarymanagement._
import sbt.librarymanagement.ivy._
-import sbt.internal.librarymanagement.{IvySbt, IvyRetrieve}
+import sbt.Logger
// Since ivy fails to resolve project dependencies, customized version is used to ignore any failure.
// This is OK as we only grab license information from the resolution metadata,
diff --git a/shared/json/src/main/resources/json-schema/draft-07.json b/shared/json/src/main/resources/json-schema/draft-07.json
new file mode 100644
index 0000000000..8875d422c2
--- /dev/null
+++ b/shared/json/src/main/resources/json-schema/draft-07.json
@@ -0,0 +1,166 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "http://json-schema.org/draft-07/schema#",
+ "title": "Core schema meta-schema",
+ "definitions": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$ref": "#" }
+ },
+ "nonNegativeInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "nonNegativeIntegerDefault0": {
+ "allOf": [
+ { "$ref": "#/definitions/nonNegativeInteger" },
+ { "default": 0 }
+ ]
+ },
+ "simpleTypes": {
+ "enum": [
+ "array",
+ "boolean",
+ "integer",
+ "null",
+ "number",
+ "object",
+ "string"
+ ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true,
+ "default": []
+ }
+ },
+ "type": ["object", "boolean"],
+ "properties": {
+ "$id": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$schema": {
+ "type": "string",
+ "format": "uri"
+ },
+ "$ref": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "$comment": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": true,
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "writeOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "examples": {
+ "type": "array",
+ "items": true
+ },
+ "multipleOf": {
+ "type": "number",
+ "exclusiveMinimum": 0
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "number"
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "number"
+ },
+ "maxLength": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "additionalItems": { "$ref": "#" },
+ "items": {
+ "anyOf": [{ "$ref": "#" }, { "$ref": "#/definitions/schemaArray" }],
+ "default": true
+ },
+ "maxItems": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "contains": { "$ref": "#" },
+ "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" },
+ "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" },
+ "required": { "$ref": "#/definitions/stringArray" },
+ "additionalProperties": { "$ref": "#" },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "propertyNames": { "format": "regex" },
+ "default": {}
+ },
+ "dependencies": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [{ "$ref": "#" }, { "$ref": "#/definitions/stringArray" }]
+ }
+ },
+ "propertyNames": { "$ref": "#" },
+ "const": true,
+ "enum": {
+ "type": "array",
+ "items": true,
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/definitions/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "format": { "type": "string" },
+ "contentMediaType": { "type": "string" },
+ "contentEncoding": { "type": "string" },
+ "if": { "$ref": "#" },
+ "then": { "$ref": "#" },
+ "else": { "$ref": "#" },
+ "allOf": { "$ref": "#/definitions/schemaArray" },
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
+ "not": { "$ref": "#" }
+ },
+ "default": true
+}
diff --git a/shared/core/src/main/scala/org/hyperledger/identus/shared/utils/Json.scala b/shared/json/src/main/scala/org/hyperledger/identus/shared/json/Json.scala
similarity index 97%
rename from shared/core/src/main/scala/org/hyperledger/identus/shared/utils/Json.scala
rename to shared/json/src/main/scala/org/hyperledger/identus/shared/json/Json.scala
index ecbc60f101..40a8a85a21 100644
--- a/shared/core/src/main/scala/org/hyperledger/identus/shared/utils/Json.scala
+++ b/shared/json/src/main/scala/org/hyperledger/identus/shared/json/Json.scala
@@ -1,4 +1,4 @@
-package org.hyperledger.identus.shared.utils
+package org.hyperledger.identus.shared.json
import com.apicatalog.jsonld.document.JsonDocument
import com.apicatalog.jsonld.http.media.MediaType
diff --git a/shared/json/src/main/scala/org/hyperledger/identus/shared/json/JsonInterop.scala b/shared/json/src/main/scala/org/hyperledger/identus/shared/json/JsonInterop.scala
new file mode 100644
index 0000000000..bd6e41ab51
--- /dev/null
+++ b/shared/json/src/main/scala/org/hyperledger/identus/shared/json/JsonInterop.scala
@@ -0,0 +1,25 @@
+package org.hyperledger.identus.shared.json
+
+import io.circe.Json as CirceJson
+import zio.json.*
+import zio.json.ast.Json as ZioJson
+
+object JsonInterop {
+ def toZioJsonAst(circeJson: CirceJson): ZioJson = {
+ val encoded = circeJson.noSpaces
+ encoded.fromJson[ZioJson] match {
+ case Left(failure) =>
+ throw Exception(s"Circe and Zio Json interop fail. Unable to convert from Circe to Zio AST. $failure")
+ case Right(value) => value
+ }
+ }
+
+ def toCirceJsonAst(zioJson: ZioJson): CirceJson = {
+ val encoded = zioJson.toJson
+ io.circe.parser.parse(encoded).left.map(_.toString) match {
+ case Left(failure) =>
+ throw Exception(s"Circe and Zio Json interop fail. Unable to convert from Zio to Circe AST. $failure")
+ case Right(value) => value
+ }
+ }
+}
diff --git a/shared/json/src/main/scala/org/hyperledger/identus/shared/json/JsonSchema.scala b/shared/json/src/main/scala/org/hyperledger/identus/shared/json/JsonSchema.scala
new file mode 100644
index 0000000000..299e6ce50c
--- /dev/null
+++ b/shared/json/src/main/scala/org/hyperledger/identus/shared/json/JsonSchema.scala
@@ -0,0 +1,189 @@
+package org.hyperledger.identus.shared.json
+
+import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
+import com.networknt.schema.*
+import com.networknt.schema.SpecVersion.VersionFlag
+import org.hyperledger.identus.shared.json.JsonSchemaError.{
+ JsonSchemaParsingError,
+ UnexpectedError,
+ UnsupportedJsonSchemaSpecVersion
+}
+import zio.*
+import zio.json.*
+import zio.json.ast.Json
+
+import scala.io.Source
+
+sealed trait JsonSchemaError {
+ def error: String
+}
+
+object JsonSchemaError {
+ case class JsonSchemaParsingError(error: String) extends JsonSchemaError
+
+ case class JsonValidationErrors(errors: Seq[String]) extends JsonSchemaError {
+ def error: String = errors.mkString(";")
+ }
+
+ case class UnsupportedJsonSchemaSpecVersion(error: String) extends JsonSchemaError
+
+ case class UnexpectedError(error: String) extends JsonSchemaError
+}
+
+trait JsonSchemaValidator {
+ def validate(claims: String): IO[JsonSchemaError, Unit]
+
+ def validate(claimsJsonNode: JsonNode): IO[JsonSchemaError, Unit] = {
+ validate(claimsJsonNode.toString)
+ }
+}
+
+case class JsonSchemaValidatorImpl(schemaValidator: JsonSchema) extends JsonSchemaValidator {
+ override def validate(jsonString: String): IO[JsonSchemaError, Unit] = {
+ import scala.jdk.CollectionConverters.*
+ for {
+ // Convert claims to JsonNode
+ jsonClaims <- JsonSchemaUtils.toJsonNode(jsonString)
+
+ // Validate claims JsonNode
+ validationMessages <- ZIO
+ .attempt(schemaValidator.validate(jsonClaims).asScala.toSeq)
+ .mapError(t => JsonSchemaError.JsonValidationErrors(Seq(t.getMessage)))
+
+ validationResult <-
+ if (validationMessages.isEmpty) ZIO.unit
+ else ZIO.fail(JsonSchemaError.JsonValidationErrors(validationMessages.map(_.getMessage)))
+ } yield validationResult
+ }
+
+}
+
+object JsonSchemaValidatorImpl {
+ def from(schema: Json): IO[JsonSchemaError, JsonSchemaValidator] = {
+ for {
+ jsonSchema <- JsonSchemaUtils.from(schema, IndexedSeq(SpecVersion.VersionFlag.V202012))
+ } yield JsonSchemaValidatorImpl(jsonSchema)
+ }
+
+ def draft7Meta: ZIO[Scope, JsonSchemaError, JsonSchemaValidator] =
+ ZIO
+ .acquireRelease {
+ ZIO
+ .attempt(Source.fromResource("json-schema/draft-07.json"))
+ .mapError(e => UnexpectedError(e.getMessage))
+ }(src => ZIO.attempt(src).orDie)
+ .map(_.mkString)
+ .flatMap(schema => JsonSchemaUtils.jsonSchemaAtVersion(schema, SpecVersion.VersionFlag.V7))
+ .map(JsonSchemaValidatorImpl(_))
+}
+
+object JsonSchemaUtils {
+
+ /** Create a json schema where the version is inferred from $schema field with optional supported version check */
+ def jsonSchema(
+ schema: String,
+ supportedVersions: IndexedSeq[VersionFlag] = IndexedSeq.empty
+ ): IO[JsonSchemaError, JsonSchema] = {
+ for {
+ jsonSchemaNode <- toJsonNode(schema)
+ specVersion <- ZIO
+ .attempt(SpecVersionDetector.detect(jsonSchemaNode))
+ .mapError(t => UnexpectedError(t.getMessage))
+ _ <-
+ if (supportedVersions.nonEmpty && !supportedVersions.contains(specVersion))
+ ZIO.fail(
+ UnsupportedJsonSchemaSpecVersion(
+ s"Unsupported JsonSchemaVersion. Current:$specVersion ExpectedOneOf:${supportedVersions.map(_.getId)}"
+ )
+ )
+ else ZIO.unit
+ jsonSchema <- jsonSchemaAtVersion(schema, specVersion)
+ } yield jsonSchema
+ }
+
+ /** Create a json schema at specific version */
+ def jsonSchemaAtVersion(
+ schema: String,
+ specVersion: VersionFlag
+ ): IO[JsonSchemaError, JsonSchema] = {
+ for {
+ jsonSchemaNode <- toJsonNode(schema)
+ mapper <- ZIO.attempt(new ObjectMapper()).mapError(t => UnexpectedError(t.getMessage))
+ factory <- ZIO
+ .attempt(JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(specVersion)).jsonMapper(mapper).build)
+ .mapError(t => UnexpectedError(t.getMessage))
+ jsonSchema <- ZIO.attempt(factory.getSchema(jsonSchemaNode)).mapError(t => UnexpectedError(t.getMessage))
+ } yield jsonSchema
+ }
+
+ def from(
+ schema: Json,
+ supportedVersions: IndexedSeq[VersionFlag] = IndexedSeq.empty
+ ): IO[JsonSchemaError, JsonSchema] = {
+ jsonSchema(schema.toString(), supportedVersions)
+ }
+
+ def toJsonNode(json: Json): IO[JsonSchemaError, JsonNode] = {
+ toJsonNode(json.toString())
+ }
+
+ def toJsonNode(json: String): IO[JsonSchemaError, JsonNode] = {
+ for {
+ mapper <- ZIO.attempt(new ObjectMapper()).mapError(t => UnexpectedError(t.getMessage))
+ jsonSchemaNode <- ZIO
+ .attempt(mapper.readTree(json))
+ .mapError(t => JsonSchemaParsingError(t.getMessage))
+ } yield jsonSchemaNode
+ }
+}
+
+class SchemaSerDes[S](jsonSchemaSchemaStr: String) {
+
+ def initialiseJsonSchema: IO[JsonSchemaError, JsonSchema] =
+ JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr)
+
+ def serializeToJsonString(instance: S)(using encoder: JsonEncoder[S]): String = {
+ instance.toJson
+ }
+
+ def serialize(instance: S)(using encoder: JsonEncoder[S]): Either[String, Json] = {
+ instance.toJsonAST
+ }
+
+ def deserialize(
+ schema: zio.json.ast.Json
+ )(using decoder: JsonDecoder[S]): IO[JsonSchemaError, S] = {
+ deserialize(schema.toString())
+ }
+
+ def deserialize(
+ jsonString: String
+ )(using decoder: JsonDecoder[S]): IO[JsonSchemaError, S] = {
+ for {
+ _ <- validate(jsonString)
+ schema <-
+ ZIO
+ .fromEither(decoder.decodeJson(jsonString))
+ .mapError(JsonSchemaError.JsonSchemaParsingError.apply)
+ } yield schema
+ }
+
+ def deserializeAsJson(jsonString: String): IO[JsonSchemaError, Json] = {
+ for {
+ _ <- validate(jsonString)
+ json <-
+ ZIO
+ .fromEither(jsonString.fromJson[Json])
+ .mapError(JsonSchemaError.JsonSchemaParsingError.apply)
+ } yield json
+ }
+
+ def validate(jsonString: String): IO[JsonSchemaError, Unit] = {
+ for {
+ jsonSchemaSchema <- JsonSchemaUtils.jsonSchema(jsonSchemaSchemaStr)
+ schemaValidator = JsonSchemaValidatorImpl(jsonSchemaSchema)
+ result <- schemaValidator.validate(jsonString)
+ } yield result
+ }
+
+}