Skip to content

Commit

Permalink
feat: migrate pollux VC JWT from Circe to zio-json (#1475)
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Voiturier <[email protected]>
  • Loading branch information
bvoiturier authored Dec 11, 2024
1 parent d0c3506 commit 248ba5f
Show file tree
Hide file tree
Showing 33 changed files with 827 additions and 1,085 deletions.
45 changes: 7 additions & 38 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ inThisBuild(
// scalacOptions += "-Ysafe-init",
// scalacOptions += "-Werror", // <=> "-Xfatal-warnings"
scalacOptions += "-Dquill.macro.log=false", // disable quill macro logs // TODO https://github.com/zio/zio-protoquill/issues/470,
scalacOptions ++= Seq("-Xmax-inlines", "50") // manually increase max-inlines above 32 (https://github.com/circe/circe/issues/2162)
scalacOptions ++= Seq("-Xmax-inlines", "50")
)
)

Expand All @@ -58,9 +58,6 @@ lazy val V = new {
val mockito = "3.2.18.0"
val monocle = "3.2.0"

// https://mvnrepository.com/artifact/io.circe/circe-core
val circe = "0.14.7"

val tapir = "1.11.7" // scala-steward:off // TODO "1.10.5"
val http4sBlaze = "0.23.15" // scala-steward:off // TODO "0.23.16"

Expand All @@ -80,7 +77,7 @@ lazy val V = new {

val scalaUri = "4.0.3"

val jwtCirceVersion = "9.4.6"
val jwtZioVersion = "9.4.6"
val zioPreludeVersion = "1.0.0-RC31"

val apollo = "1.3.5"
Expand Down Expand Up @@ -123,12 +120,8 @@ lazy val D = new {
val zioConfigMagnolia: ModuleID = "dev.zio" %% "zio-config-magnolia" % V.zioConfig
val zioConfigTypesafe: ModuleID = "dev.zio" %% "zio-config-typesafe" % V.zioConfig

val circeCore: ModuleID = "io.circe" %% "circe-core" % V.circe
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 jwtZio = "com.github.jwt-scala" %% "jwt-zio-json" % V.jwtZioVersion
val jsonCanonicalization: ModuleID = "io.github.erdtman" % "java-json-canonicalization" % "1.1"
val titaniumJsonLd: ModuleID = "com.apicatalog" % "titanium-json-ld" % "1.4.0"
val jakartaJson: ModuleID = "org.glassfish" % "jakarta.json" % "2.0.1"
Expand Down Expand Up @@ -156,7 +149,6 @@ lazy val D = new {
"com.github.dasniko" % "testcontainers-keycloak" % V.testContainersJavaKeycloak % Test

val doobiePostgres: ModuleID = "org.tpolecat" %% "doobie-postgres" % V.doobie
val doobiePostgresCirce: ModuleID = "org.tpolecat" %% "doobie-postgres-circe" % V.doobie
val doobieHikari: ModuleID = "org.tpolecat" %% "doobie-hikari" % V.doobie
val flyway: ModuleID = "org.flywaydb" % "flyway-core" % V.flyway

Expand Down Expand Up @@ -185,7 +177,7 @@ lazy val D = new {

// LIST of Dependencies
val doobieDependencies: Seq[ModuleID] =
Seq(doobiePostgres, doobiePostgresCirce, doobieHikari, flyway)
Seq(doobiePostgres, doobieHikari, flyway)
}

lazy val D_Shared = new {
Expand All @@ -210,9 +202,6 @@ lazy val D_SharedJson = new {
Seq(
D.zio,
D.zioJson,
D.circeCore,
D.circeGeneric,
D.circeParser,
D.jsonCanonicalization,
D.titaniumJsonLd,
D.jakartaJson,
Expand Down Expand Up @@ -274,9 +263,6 @@ lazy val D_Castor = new {
D.zioMock,
D.zioTestSbt,
D.zioTestMagnolia,
D.circeCore,
D.circeGeneric,
D.circeParser
)

// Project Dependencies
Expand Down Expand Up @@ -343,7 +329,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 :+ D.networkntJsonSchemaValidator :+ D.nimbusJwt :+ D.scalaTest
zioDependencies :+ D.jwtZio :+ D.networkntJsonSchemaValidator :+ D.nimbusJwt :+ D.scalaTest

// Project Dependencies
lazy val polluxVcJwtDependencies: Seq[ModuleID] = baseDependencies
Expand Down Expand Up @@ -426,7 +412,7 @@ lazy val D_CloudAgent = new {
lazy val keyManagementDependencies: Seq[ModuleID] =
baseDependencies ++ D.doobieDependencies ++ Seq(D.zioCatsInterop, D.zioMock, vaultDriver)

lazy val iamDependencies: Seq[ModuleID] = Seq(keycloakAuthz, D.jwtCirce)
lazy val iamDependencies: Seq[ModuleID] = Seq(keycloakAuthz, D.jwtZio)

lazy val serverDependencies: Seq[ModuleID] =
baseDependencies ++ tapirDependencies ++ postgresDependencies ++ Seq(
Expand Down Expand Up @@ -533,13 +519,7 @@ lazy val models = project
.configure(commonConfigure)
.settings(name := "mercury-data-models")
.settings(
libraryDependencies ++= Seq(D.zio),
libraryDependencies ++= Seq(
D.circeCore,
D.circeGeneric,
D.circeParser
), // TODO try to remove this from this module
// libraryDependencies += D.didScala
libraryDependencies ++= Seq(D.zio)
)
.settings(libraryDependencies += D.nimbusJwt) // FIXME just for the DidAgent
.dependsOn(shared)
Expand All @@ -561,7 +541,6 @@ lazy val protocolConnection = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-connection")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models, protocolInvitation)

Expand All @@ -570,7 +549,6 @@ lazy val protocolCoordinateMediation = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-coordinate-mediation")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models)

Expand All @@ -579,7 +557,6 @@ lazy val protocolDidExchange = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-did-exchange")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.dependsOn(models, protocolInvitation)

lazy val protocolInvitation = project
Expand All @@ -589,9 +566,6 @@ lazy val protocolInvitation = project
.settings(libraryDependencies += D.zio)
.settings(
libraryDependencies ++= Seq(
D.circeCore,
D.circeGeneric,
D.circeParser,
D.munit,
D.munitZio
)
Expand All @@ -611,7 +585,6 @@ lazy val protocolLogin = project
.settings(name := "mercury-protocol-outofband-login")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models)

Expand All @@ -634,7 +607,6 @@ lazy val protocolIssueCredential = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-issue-credential")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models, protocolInvitation)

Expand All @@ -643,7 +615,6 @@ lazy val protocolRevocationNotification = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-revocation-notification")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models)

Expand All @@ -652,7 +623,6 @@ lazy val protocolPresentProof = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-present-proof")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models, protocolInvitation)

Expand All @@ -667,7 +637,6 @@ lazy val protocolTrustPing = project
.configure(commonConfigure)
.settings(name := "mercury-protocol-trust-ping")
.settings(libraryDependencies += D.zio)
.settings(libraryDependencies ++= Seq(D.circeCore, D.circeGeneric, D.circeParser))
.settings(libraryDependencies += D.munitZio)
.dependsOn(models)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import org.hyperledger.identus.pollux.core.model.{CredInStatusList, CredentialSt
import org.hyperledger.identus.pollux.core.service.{CredentialService, CredentialStatusListService}
import org.hyperledger.identus.pollux.vc.jwt.revocation.{BitString, VCStatusList2021, VCStatusList2021Error}
import org.hyperledger.identus.resolvers.DIDResolver
import org.hyperledger.identus.shared.json.JsonInterop
import org.hyperledger.identus.shared.messaging
import org.hyperledger.identus.shared.messaging.{Message, Producer, WalletIdAndRecordId}
import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId}
import org.hyperledger.identus.shared.utils.DurationOps.toMetricsSeconds
import zio.*
import zio.json.{DecoderOps, EncoderOps}
import zio.json.ast.Json
import zio.json.DecoderOps
import zio.metrics.Metric

import java.util.UUID
Expand Down Expand Up @@ -78,7 +77,7 @@ object StatusListJobs extends BackgroundJobsHelper {
vcStatusListCredJson <- ZIO.fromEither(vcStatusListCredString.fromJson[Json])
issuer <- createJwtVcIssuer(statusListWithCreds.issuer, VerificationRelationship.AssertionMethod, None)
vcStatusListCred <- VCStatusList2021
.decodeFromJson(JsonInterop.toCirceJsonAst(vcStatusListCredJson), issuer)
.decodeFromJson(vcStatusListCredJson, issuer)
.mapError(x => new Throwable(x.msg))
bitString <- vcStatusListCred.getBitString.mapError(x => new Throwable(x.msg))
_ <- ZIO.collectAll(
Expand All @@ -99,7 +98,7 @@ object StatusListJobs extends BackgroundJobsHelper {
case VCStatusList2021Error.EncodingError(msg: String) => new Throwable(msg)
case VCStatusList2021Error.DecodingError(msg: String) => new Throwable(msg)
}
vcStatusListCredJsonString <- updatedVcStatusListCred.toJsonWithEmbeddedProof.map(_.spaces2)
vcStatusListCredJsonString <- updatedVcStatusListCred.toJsonWithEmbeddedProof.map(_.toJson)
_ <- credentialStatusListService.updateStatusListCredential(
statusListWithCreds.id,
vcStatusListCredJsonString
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.hyperledger.identus.iam.authentication.AuthenticationError.{
InvalidCredentials
}
import org.hyperledger.identus.shared.utils.Traverse.*
import pdi.jwt.{JwtCirce, JwtClaim, JwtOptions}
import pdi.jwt.{JwtClaim, JwtOptions, JwtZIOJson}
import zio.*
import zio.json.ast.Json

Expand Down Expand Up @@ -61,7 +61,7 @@ final class AccessToken private (token: String, claims: JwtClaim, rolesClaimPath

object AccessToken {
def fromString(token: String, rolesClaimPath: Seq[String] = Nil): Either[String, AccessToken] =
JwtCirce
JwtZIOJson
.decode(token, JwtOptions(false, false, false))
.map(claims => AccessToken(token, claims, rolesClaimPath))
.toEither
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import org.hyperledger.identus.castor.core.model.did.{DID, DIDUrl, PrismDID, Ver
import org.hyperledger.identus.oid4vci.domain.{IssuanceSession, Openid4VCIProofJwtOps}
import org.hyperledger.identus.oid4vci.http.*
import org.hyperledger.identus.oid4vci.storage.IssuanceSessionStorage
import org.hyperledger.identus.pollux.core.model.primitives.UriString
import org.hyperledger.identus.pollux.core.model.primitives.UriString.toUriString
import org.hyperledger.identus.pollux.core.model.schema.CredentialSchema
import org.hyperledger.identus.pollux.core.service.{
Expand All @@ -23,7 +22,6 @@ import org.hyperledger.identus.pollux.vc.jwt.{
*
}
import org.hyperledger.identus.shared.http.UriResolver
import org.hyperledger.identus.shared.json.JsonInterop
import org.hyperledger.identus.shared.models.*
import zio.*
import zio.json.ast.Json
Expand Down Expand Up @@ -206,7 +204,7 @@ case class OIDCCredentialIssuerServiceImpl(
issuanceDate = Instant.now(),
maybeExpirationDate = None, // TODO: Add expiration date
maybeCredentialSchema = None, // TODO: Add schema from schema registry
credentialSubject = JsonInterop.toCirceJsonAst(buildCredentialSubject(subjectDid, claims)),
credentialSubject = buildCredentialSubject(subjectDid, claims),
maybeCredentialStatus = None, // TODO: Add credential status
maybeRefreshService = None, // TODO: Add refresh service
maybeEvidence = None, // TODO: Add evidence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import org.hyperledger.identus.api.http.ErrorResponse
import org.hyperledger.identus.castor.core.model.did.{DIDData, DIDMetadata, PrismDIDOperation, VerificationRelationship}
import org.hyperledger.identus.castor.core.service.MockDIDService
import org.hyperledger.identus.connect.core.model.ConnectionRecord
import org.hyperledger.identus.connect.core.model.ConnectionRecord.ProtocolState
import org.hyperledger.identus.connect.core.service
import org.hyperledger.identus.connect.core.service.MockConnectionService
import org.hyperledger.identus.container.util.MigrationAspects.migrate
import org.hyperledger.identus.iam.authentication.AuthenticatorWithAuthZ
Expand All @@ -21,16 +19,16 @@ import org.hyperledger.identus.mercury.model.DidId
import org.hyperledger.identus.mercury.protocol.connection.ConnectionResponse
import org.hyperledger.identus.mercury.protocol.invitation.v2.Invitation
import org.hyperledger.identus.pollux.core.model.{CredentialFormat, DidCommID, IssueCredentialRecord}
import org.hyperledger.identus.pollux.core.model.IssueCredentialRecord.{ProtocolState, Role}
import org.hyperledger.identus.pollux.core.repository.CredentialDefinitionRepositoryInMemory
import org.hyperledger.identus.pollux.core.service.{CredentialDefinitionServiceImpl, MockCredentialService}
import org.hyperledger.identus.pollux.core.service.uriResolvers.ResourceUrlResolver
import org.hyperledger.identus.shared.models.{KeyId, WalletId}
import sttp.client3.{basicRequest, DeserializationException, UriContext}
import sttp.client3.{basicRequest, UriContext}
import sttp.client3.ziojson.*
import sttp.model.StatusCode
import zio.*
import zio.json.EncoderOps
import zio.json.{DecoderOps, EncoderOps}
import zio.json.ast.Json
import zio.mock.Expectation
import zio.test.*
import zio.test.Assertion.*
Expand All @@ -54,7 +52,7 @@ object IssueControllerImplSpec extends ZIOSpecDefault with IssueControllerTestTo
schemaId = Some("mySchemaId"),
credentialDefinitionId = Some(UUID.fromString("123e4567-e89b-12d3-a456-426614174000")),
credentialFormat = Some("JWT"),
claims = json.toJsonAST.toOption.get,
claims = json.fromJson[Json].toOption.get,
automaticIssuance = Some(true),
issuingDID =
"did:prism:332518729a7b7805f73a788e0944802527911901d9b7c16152281be9bc62d944:CosBCogBEkkKFW15LWtleS1hdXRoZW50aWNhdGlvbhAESi4KCXNlY3AyNTZrMRIhAuYoRIefsLhkvYwHz8gDtkG2b0kaZTDOLj_SExWX1fOXEjsKB21hc3RlcjAQAUouCglzZWNwMjU2azESIQLOzab8f0ibt1P0zdMfoWDQTSlPc8_tkV9Jk5BBsXB8fA",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package org.hyperledger.identus.verification.controller

import org.hyperledger.identus.agent.walletapi.model.BaseEntity
import org.hyperledger.identus.agent.walletapi.service.{ManagedDIDService, MockManagedDIDService}
import org.hyperledger.identus.agent.walletapi.service.MockManagedDIDService
import org.hyperledger.identus.castor.core.service.MockDIDService
import org.hyperledger.identus.iam.authentication.AuthenticatorWithAuthZ
import org.hyperledger.identus.pollux.vc.jwt.*
import org.hyperledger.identus.pollux.vc.jwt.CredentialPayload.Implicits.*
import org.hyperledger.identus.shared.json.JsonInterop
import org.hyperledger.identus.verification.controller.http.*
import sttp.client3.{basicRequest, DeserializationException, Response, UriContext}
import sttp.client3.ziojson.*
Expand Down Expand Up @@ -47,12 +45,10 @@ object VcVerificationControllerImplSpec extends ZIOSpecDefault with VcVerificati
`type` = "JsonSchemaValidator2018"
)
),
credentialSubject = JsonInterop.toCirceJsonAst(
Json.Obj(
"userName" -> Json.Str("Bob"),
"age" -> Json.Num(42),
"email" -> Json.Str("email")
)
credentialSubject = Json.Obj(
"userName" -> Json.Str("Bob"),
"age" -> Json.Num(42),
"email" -> Json.Str("email")
),
maybeCredentialStatus = Some(
CredentialStatus(
Expand All @@ -73,7 +69,7 @@ object VcVerificationControllerImplSpec extends ZIOSpecDefault with VcVerificati
maybeTermsOfUse = Option.empty,
aud = Set(verifier)
).toJwtCredentialPayload
signedJwtCredential = issuer.signer.encode(io.circe.syntax.EncoderOps(jwtCredentialPayload).asJson)
signedJwtCredential = issuer.signer.encode(jwtCredentialPayload.toJsonAST.toOption.get)
authenticator <- ZIO.service[AuthenticatorWithAuthZ[BaseEntity]]
backend = httpBackend(vcVerificationController, authenticator)
request = List(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.hyperledger.identus.pollux.vc.jwt.revocation.BitStringError.{
import org.hyperledger.identus.pollux.vc.jwt.Issuer
import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId}
import zio.*
import zio.json.EncoderOps

import java.util.UUID

Expand All @@ -36,7 +37,7 @@ trait CredentialStatusListRepository {
.mapError(x => new Throwable(x.msg))

credentialWithEmbeddedProof <- emptyStatusListCredential.toJsonWithEmbeddedProof
} yield credentialWithEmbeddedProof.spaces2
} yield credentialWithEmbeddedProof.toJson
}

def getCredentialStatusListIds: UIO[Seq[(WalletId, UUID)]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ import org.hyperledger.identus.pollux.core.repository.{CredentialRepository, Cre
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.pollux.vc.jwt.PresentationPayload.Implicits.*
import org.hyperledger.identus.shared.crypto.{Ed25519KeyPair, Secp256k1KeyPair}
import org.hyperledger.identus.shared.http.UriResolver
import org.hyperledger.identus.shared.json.JsonInterop
import org.hyperledger.identus.shared.messaging.{Producer, WalletIdAndRecordId}
import org.hyperledger.identus.shared.models.*
import org.hyperledger.identus.shared.models.Failure.orDieAsUnmanagedFailure
Expand Down Expand Up @@ -1219,7 +1217,7 @@ class CredentialServiceImpl(
ids.map(id => org.hyperledger.identus.pollux.vc.jwt.CredentialSchema(id, VC_JSON_SCHEMA_TYPE))
),
maybeCredentialStatus = Some(credentialStatus),
credentialSubject = JsonInterop.toCirceJsonAst(claims.add("id", Json.Str(jwtPresentation.iss))),
credentialSubject = claims.add("id", Json.Str(jwtPresentation.iss)),
maybeRefreshService = None,
maybeEvidence = None,
maybeTermsOfUse = None,
Expand Down Expand Up @@ -1455,7 +1453,7 @@ class CredentialServiceImpl(
.fromOption(offer.attachments.headOption)
.orElse(ZIO.dieMessage(s"Attachments not found in record: ${record.id}"))
json <- attachmentDescriptor.data match
case JsonData(json) => ZIO.succeed(json.toJsonAST.toOption.get)
case JsonData(json) => ZIO.succeed(json)
case _ => ZIO.dieMessage(s"Attachment doesn't contain JsonData: ${record.id}")
maybeOptions <- ZIO
.fromEither(json.as[PresentationAttachment].map(_.options))
Expand Down
Loading

0 comments on commit 248ba5f

Please sign in to comment.