Skip to content

Commit

Permalink
feat: URL or Object as Issuer (#1321)
Browse files Browse the repository at this point in the history
Signed-off-by: Bassam Riman <[email protected]>
  • Loading branch information
CryptoKnightIOG authored Sep 5, 2024
1 parent 3b3da2c commit 0c53bba
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.hyperledger.identus.pollux.vc.jwt.{
W3cCredentialPayload,
*
}
import org.hyperledger.identus.pollux.vc.jwt.DID.*
import org.hyperledger.identus.shared.models.*
import zio.*

Expand Down Expand Up @@ -193,7 +194,7 @@ case class OIDCCredentialIssuerServiceImpl(
`type` = Set(
"VerifiableCredential"
) ++ credentialDefinition.`type`, // TODO: This information should come from Schema registry by record.schemaId
issuer = issuerDid,
issuer = Left(issuerDid.value),
issuanceDate = Instant.now(),
maybeExpirationDate = None, // TODO: Add expiration date
maybeCredentialSchema = None, // TODO: Add schema from schema registry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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.pollux.vc.jwt.DID.*
import org.hyperledger.identus.verification.controller.http.*
import sttp.client3.{basicRequest, DeserializationException, Response, UriContext}
import sttp.client3.ziojson.*
Expand Down Expand Up @@ -36,7 +37,7 @@ object VcVerificationControllerImplSpec extends ZIOSpecDefault with VcVerificati
`@context` = Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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.DID.*
import org.hyperledger.identus.shared.crypto.{Ed25519KeyPair, Ed25519PublicKey, Secp256k1KeyPair}
import org.hyperledger.identus.shared.http.{DataUrlResolver, GenericUriResolver}
import org.hyperledger.identus.shared.models.*
Expand Down Expand Up @@ -1134,7 +1135,7 @@ class CredentialServiceImpl(
maybeId = None,
`type` =
Set("VerifiableCredential"), // TODO: This information should come from Schema registry by record.schemaId
issuer = jwtIssuer.did,
issuer = Left(jwtIssuer.did.value),
issuanceDate = issuanceDate,
maybeExpirationDate = record.validityPeriod.map(sec => issuanceDate.plusSeconds(sec.toLong)),
maybeCredentialSchema =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.hyperledger.identus.castor.core.service.MockDIDService
import org.hyperledger.identus.pollux.core.service.ResourceURIDereferencerImpl
import org.hyperledger.identus.pollux.vc.jwt.*
import org.hyperledger.identus.pollux.vc.jwt.CredentialPayload.Implicits.*
import org.hyperledger.identus.pollux.vc.jwt.DID.*
import org.hyperledger.identus.shared.models.{WalletAccessContext, WalletId}
import zio.*
import zio.test.*
Expand All @@ -27,7 +28,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -93,7 +94,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -158,7 +159,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -223,7 +224,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -293,7 +294,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -363,7 +364,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -429,7 +430,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -495,7 +496,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down Expand Up @@ -561,7 +562,7 @@ object VcVerificationServiceImplSpec extends ZIOSpecDefault with VcVerificationS
Set("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
maybeId = Some("http://example.edu/credentials/3732"),
`type` = Set("VerifiableCredential", "UniversityDegreeCredential"),
issuer = issuer.did,
issuer = Left(issuer.did.value),
issuanceDate = Instant.parse("2010-01-01T00:00:00Z"),
maybeExpirationDate = Some(Instant.parse("2010-01-12T00:00:00Z")),
maybeValidFrom = Some(Instant.parse("2010-01-12T00:00:00Z")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ case class CredentialSchema(
`type`: String
)

case class CredentialIssuer(id: String)

sealed trait CredentialPayload {
def maybeSub: Option[String]

Expand All @@ -83,7 +85,7 @@ sealed trait CredentialPayload {

def maybeValidUntil: Option[Instant]

def iss: String
def issuer: Either[String, CredentialIssuer]

def maybeCredentialStatus: Option[CredentialStatus]

Expand All @@ -99,7 +101,7 @@ sealed trait CredentialPayload {

def toJwtCredentialPayload: JwtCredentialPayload =
JwtCredentialPayload(
iss = iss,
iss = issuer.fold(identity, _.id),
maybeSub = maybeSub,
vc = JwtVc(
`@context` = `@context`,
Expand All @@ -111,7 +113,8 @@ sealed trait CredentialPayload {
maybeEvidence = maybeEvidence,
maybeTermsOfUse = maybeTermsOfUse,
maybeValidFrom = maybeValidFrom,
maybeValidUntil = maybeValidUntil
maybeValidUntil = maybeValidUntil,
maybeIssuer = Some(issuer),
),
nbf = nbf,
aud = aud,
Expand All @@ -124,7 +127,7 @@ sealed trait CredentialPayload {
`@context` = `@context`,
maybeId = maybeJti,
`type` = `type`,
issuer = DID(iss),
issuer = issuer,
issuanceDate = nbf,
maybeExpirationDate = maybeExp,
maybeCredentialSchema = maybeCredentialSchema,
Expand All @@ -146,14 +149,15 @@ case class JwtVc(
credentialSubject: Json,
maybeValidFrom: Option[Instant],
maybeValidUntil: Option[Instant],
maybeIssuer: Option[Either[String, CredentialIssuer]],
maybeCredentialStatus: Option[CredentialStatus],
maybeRefreshService: Option[RefreshService],
maybeEvidence: Option[Json],
maybeTermsOfUse: Option[Json]
)

case class JwtCredentialPayload(
override val iss: String,
iss: String,
override val maybeSub: Option[String],
vc: JwtVc,
override val nbf: Instant,
Expand All @@ -171,13 +175,14 @@ case class JwtCredentialPayload(
override val credentialSubject = vc.credentialSubject
override val maybeValidFrom = vc.maybeValidFrom
override val maybeValidUntil = vc.maybeValidUntil
override val issuer = vc.maybeIssuer.getOrElse(Left(iss))
}

case class W3cCredentialPayload(
override val `@context`: Set[String],
override val `type`: Set[String],
maybeId: Option[String],
issuer: DID,
issuer: Either[String, CredentialIssuer],
issuanceDate: Instant,
maybeExpirationDate: Option[Instant],
override val maybeCredentialSchema: Option[CredentialSchema],
Expand All @@ -194,7 +199,6 @@ case class W3cCredentialPayload(
override val maybeJti = maybeId
override val nbf = issuanceDate
override val maybeExp = maybeExpirationDate
override val iss = issuer.value
}

object CredentialPayload {
Expand Down Expand Up @@ -222,6 +226,13 @@ object CredentialPayload {
("type", credentialSchema.`type`.asJson)
)

implicit val credentialIssuerEncoder: Encoder[CredentialIssuer] =
(credentialIssuer: CredentialIssuer) =>
Json
.obj(
("id", credentialIssuer.id.asJson)
)

implicit val credentialStatusPurposeEncoder: Encoder[StatusPurpose] = (a: StatusPurpose) => a.toString.asJson

implicit val credentialStatusEncoder: Encoder[CredentialStatus] =
Expand All @@ -235,6 +246,14 @@ object CredentialPayload {
("statusListCredential", credentialStatus.statusListCredential.asJson)
)

implicit val eitherStringOrCredentialIssuerEncoder: Encoder[Either[String, CredentialIssuer]] = {
case Left(value) => Json.fromString(value)
case Right(issuer) => issuer.asJson
}

implicit val eitherStringOrCredentialIssuerDecoder: Decoder[Either[String, CredentialIssuer]] =
Decoder[String].map(Left(_)).or(Decoder[CredentialIssuer].map(Right(_)))

implicit val w3cCredentialPayloadEncoder: Encoder[W3cCredentialPayload] =
(w3cCredentialPayload: W3cCredentialPayload) =>
Json
Expand Down Expand Up @@ -272,7 +291,8 @@ object CredentialPayload {
("evidence", jwtVc.maybeEvidence.asJson),
("termsOfUse", jwtVc.maybeTermsOfUse.asJson),
("validFrom", jwtVc.maybeValidFrom.asJson),
("validUntil", jwtVc.maybeValidUntil.asJson)
("validUntil", jwtVc.maybeValidUntil.asJson),
("issuer", jwtVc.maybeIssuer.asJson)
)
.deepDropNullValues
.dropEmptyValues
Expand Down Expand Up @@ -324,6 +344,14 @@ object CredentialPayload {
CredentialSchema(id = id, `type` = `type`)
}

implicit val credentialIssuerDecoder: Decoder[CredentialIssuer] =
(c: HCursor) =>
for {
id <- c.downField("id").as[String]
} yield {
CredentialIssuer(id = id)
}

implicit val credentialStatusPurposeDecoder: Decoder[StatusPurpose] = (c: HCursor) =>
Decoder.decodeString(c).flatMap { str =>
Try(StatusPurpose.valueOf(str)).toEither.leftMap { _ =>
Expand Down Expand Up @@ -361,7 +389,7 @@ object CredentialPayload {
.as[Set[String]]
.orElse(c.downField("type").as[String].map(Set(_)))
maybeId <- c.downField("id").as[Option[String]]
issuer <- c.downField("issuer").as[String]
issuer <- c.downField("issuer").as[Either[String, CredentialIssuer]]
issuanceDate <- c.downField("issuanceDate").as[Instant]
maybeExpirationDate <- c.downField("expirationDate").as[Option[Instant]]
maybeValidFrom <- c.downField("validFrom").as[Option[Instant]]
Expand All @@ -377,7 +405,7 @@ object CredentialPayload {
`@context` = `@context`,
`type` = `type`,
maybeId = maybeId,
issuer = DID(issuer),
issuer = issuer,
issuanceDate = issuanceDate,
maybeExpirationDate = maybeExpirationDate,
maybeValidFrom = maybeValidFrom,
Expand Down Expand Up @@ -411,6 +439,7 @@ object CredentialPayload {
maybeTermsOfUse <- c.downField("termsOfUse").as[Option[Json]]
maybeValidFrom <- c.downField("validFrom").as[Option[Instant]]
maybeValidUntil <- c.downField("validUntil").as[Option[Instant]]
maybeIssuer <- c.downField("issuer").as[Option[Either[String, CredentialIssuer]]]
} yield {
JwtVc(
`@context` = `@context`,
Expand All @@ -423,6 +452,7 @@ object CredentialPayload {
maybeTermsOfUse = maybeTermsOfUse,
maybeValidFrom = maybeValidFrom,
maybeValidUntil = maybeValidUntil,
maybeIssuer = maybeIssuer
)
}

Expand Down Expand Up @@ -853,7 +883,7 @@ object W3CCredential {
)(didResolver: DidResolver): IO[String, Validation[String, Unit]] = {
JWTVerification.validateEncodedJwt(payload.proof.jwt, proofPurpose)(didResolver: DidResolver)(claim =>
Validation.fromEither(decode[W3cCredentialPayload](claim).left.map(_.toString))
)(_.issuer.value)
)(_.issuer.fold(identity, _.id))
}

def verifyDates(w3cPayload: W3cVerifiableCredentialPayload, leeway: TemporalAmount)(implicit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.circe.{Json, JsonObject}
import io.circe.syntax.*
import org.hyperledger.identus.pollux.vc.jwt.*
import org.hyperledger.identus.pollux.vc.jwt.revocation.VCStatusList2021Error.{DecodingError, EncodingError}
import org.hyperledger.identus.pollux.vc.jwt.DID.*
import zio.*

import java.time.Instant
Expand Down Expand Up @@ -61,7 +62,7 @@ object VCStatusList2021 {
),
maybeId = Some(vcId),
`type` = Set("VerifiableCredential", "StatusList2021Credential"),
issuer = jwtIssuer.did,
issuer = Left(jwtIssuer.did.value),
issuanceDate = Instant.now,
maybeExpirationDate = None,
maybeCredentialSchema = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ object JWTVerificationTest extends ZIOSpecDefault {
maybeEvidence = None,
maybeTermsOfUse = None,
maybeValidFrom = Some(validFrom),
maybeValidUntil = Some(validUntil)
maybeValidUntil = Some(validUntil),
maybeIssuer = Some(Left(issuer.issuer.did.value))
),
nbf = jwtCredentialNbf, // ISSUANCE DATE
aud = Set.empty,
Expand Down

0 comments on commit 0c53bba

Please sign in to comment.