diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DIDData.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DIDData.scala index 68aca3e349..ce00f8c8c4 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DIDData.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/DIDData.scala @@ -1,5 +1,6 @@ package io.iohk.atala.castor.core.model.did +import java.time.Instant import scala.collection.immutable.ArraySeq final case class DIDData( @@ -12,5 +13,8 @@ final case class DIDData( final case class DIDMetadata( lastOperationHash: ArraySeq[Byte], - deactivated: Boolean + canonicalId: Option[CanonicalPrismDID], + deactivated: Boolean, + created: Option[Instant], + updated: Option[Instant] ) diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/EllipticCurve.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/EllipticCurve.scala index 2b19a501fd..a45986022f 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/EllipticCurve.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/EllipticCurve.scala @@ -5,6 +5,8 @@ package io.iohk.atala.castor.core.model.did // in the "JSON Web Key Elliptic Curve" section enum EllipticCurve(val name: String) { case SECP256K1 extends EllipticCurve("secp256k1") + case ED25519 extends EllipticCurve("Ed25519") + case X25519 extends EllipticCurve("X25519") } object EllipticCurve { diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PublicKey.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PublicKey.scala index 263d33eacc..55867ce39f 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PublicKey.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/PublicKey.scala @@ -33,14 +33,20 @@ object PublicKeyData { crv: EllipticCurve, data: Base64UrlString ) extends PublicKeyData { - def toUncompressedKeyData: ECKeyData = { - val prism14PublicKey = EC.INSTANCE.toPublicKeyFromCompressed(data.toByteArray) - val ecPoint = prism14PublicKey.getCurvePoint() - ECKeyData( - crv = crv, - x = Base64UrlString.fromByteArray(ecPoint.getX().bytes()), - y = Base64UrlString.fromByteArray(ecPoint.getY().bytes()) - ) + def toUncompressedKeyData: Option[ECKeyData] = { + crv match { + case EllipticCurve.SECP256K1 => + val prism14PublicKey = EC.INSTANCE.toPublicKeyFromCompressed(data.toByteArray) + val ecPoint = prism14PublicKey.getCurvePoint() + Some( + ECKeyData( + crv = crv, + x = Base64UrlString.fromByteArray(ecPoint.getX().bytes()), + y = Base64UrlString.fromByteArray(ecPoint.getY().bytes()) + ) + ) + case _ => None + } } } } diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDDocumentRepr.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDDocumentRepr.scala index 28492d4194..37dc1d23ff 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDDocumentRepr.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDDocumentRepr.scala @@ -20,7 +20,7 @@ type PublicKeyReprOrRef = PublicKeyRepr | String final case class PublicKeyRepr( id: String, - `type`: "EcdsaSecp256k1VerificationKey2019", // TODO: use JsonWebKey2020 (ATL-3788) + `type`: "JsonWebKey2020", controller: String, publicKeyJwk: PublicKeyJwk ) @@ -31,4 +31,4 @@ final case class ServiceRepr( serviceEndpoint: Json ) -final case class PublicKeyJwk(kty: "EC", crv: String, x: String, y: String) +final case class PublicKeyJwk(kty: String, crv: String, x: Option[String], y: Option[String]) diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDMetadata.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDMetadata.scala index a35101838e..a2e5cdb72b 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDMetadata.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/DIDMetadata.scala @@ -12,4 +12,10 @@ enum DIDResolutionErrorRepr(val value: String, val errorMessage: Option[String]) case UnsupportedPublicKeyType extends DIDResolutionErrorRepr("unsupportedPublicKeyType", None) } -final case class DIDDocumentMetadataRepr(deactivated: Boolean, canonicalId: String, versionId: String) +final case class DIDDocumentMetadataRepr( + deactivated: Boolean, + canonicalId: Option[String], + versionId: String, + created: Option[String], + updated: Option[String] +) diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelper.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelper.scala index e66437f46b..6b74060b4f 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelper.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelper.scala @@ -14,16 +14,29 @@ import io.iohk.atala.castor.core.model.did.ServiceType import io.circe.Json import io.iohk.atala.castor.core.model.did.ServiceEndpoint import io.iohk.atala.castor.core.model.did.ServiceEndpoint.UriOrJsonEndpoint +import io.iohk.atala.castor.core.model.did.EllipticCurve +import java.time.format.DateTimeFormatter +import java.time.ZoneOffset +import java.time.Instant object W3CModelHelper extends W3CModelHelper private[castor] trait W3CModelHelper { + private val XML_DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") + + private def toXmlDateTime(time: Instant): String = { + val zonedDateTime = time.atZone(ZoneOffset.UTC) + XML_DATETIME_FORMATTER.format(zonedDateTime) + } + extension (didMetadata: DIDMetadata) { - def toW3C(did: PrismDID): DIDDocumentMetadataRepr = DIDDocumentMetadataRepr( + def toW3C: DIDDocumentMetadataRepr = DIDDocumentMetadataRepr( deactivated = didMetadata.deactivated, - canonicalId = did.asCanonical.toString, - versionId = HexString.fromByteArray(didMetadata.lastOperationHash.toArray).toString + canonicalId = didMetadata.canonicalId.map(_.toString), + versionId = HexString.fromByteArray(didMetadata.lastOperationHash.toArray).toString, + created = didMetadata.created.map(toXmlDateTime), + updated = didMetadata.updated.map(toXmlDateTime) ) } @@ -105,29 +118,62 @@ private[castor] trait W3CModelHelper { } } + // FIXME: do we need to support uncompress for OKP key types? extension (publicKey: PublicKey) { - def toW3C(did: PrismDID, controller: PrismDID): PublicKeyRepr = PublicKeyRepr( - id = s"${did.toString}#${publicKey.id}", - `type` = "EcdsaSecp256k1VerificationKey2019", - controller = controller.toString, - publicKeyJwk = publicKey.publicKeyData match { - case PublicKeyData.ECKeyData(crv, x, y) => + def toW3C(did: PrismDID, controller: PrismDID): PublicKeyRepr = { + val curve = publicKey.publicKeyData match { + case PublicKeyData.ECCompressedKeyData(crv, _) => crv + case PublicKeyData.ECKeyData(crv, _, _) => crv + } + val publicKeyJwk = curve match { + case EllipticCurve.SECP256K1 => secp256k1Repr(publicKey.publicKeyData) + case EllipticCurve.ED25519 => okpPublicKeyRepr(publicKey.publicKeyData) + case EllipticCurve.X25519 => okpPublicKeyRepr(publicKey.publicKeyData) + } + PublicKeyRepr( + id = s"${did.toString}#${publicKey.id}", + `type` = "JsonWebKey2020", + controller = controller.toString, + publicKeyJwk = publicKeyJwk + ) + } + + private def okpPublicKeyRepr(pk: PublicKeyData): PublicKeyJwk = { + pk match { + case PublicKeyData.ECCompressedKeyData(crv, data) => PublicKeyJwk( - kty = "EC", + kty = "OKP", crv = crv.name, - x = x.toStringNoPadding, - y = y.toStringNoPadding + x = Some(data.toStringNoPadding), + y = None ) + case PublicKeyData.ECKeyData(crv, _, _) => + throw Exception(s"Uncompressed key for curve ${crv.name} is not supported") + } + } + + private def secp256k1Repr(pk: PublicKeyData): PublicKeyJwk = { + pk match { case pk: PublicKeyData.ECCompressedKeyData => - val uncompressed = pk.toUncompressedKeyData + val uncomporessed = pk.toUncompressedKeyData.getOrElse( + throw Exception(s"Conversion to uncompress key is not supported for curve ${pk.crv.name}") + ) PublicKeyJwk( kty = "EC", - crv = uncompressed.crv.name, - x = uncompressed.x.toStringNoPadding, - y = uncompressed.y.toStringNoPadding + crv = uncomporessed.crv.name, + x = Some(uncomporessed.x.toStringNoPadding), + y = Some(uncomporessed.y.toStringNoPadding) ) + case PublicKeyData.ECKeyData(crv, x, y) => + PublicKeyJwk( + kty = "EC", + crv = crv.name, + x = Some(x.toStringNoPadding), + y = Some(y.toStringNoPadding) + ) + } - ) + } } } diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/package.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/package.scala index 92ce0e8cba..a32f1aaa1d 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/package.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/model/did/w3c/package.scala @@ -37,7 +37,7 @@ package object w3c { } yield { // https://www.w3.org/TR/did-core/#dfn-diddocument // The value of id in the resolved DID document MUST match the DID that was resolved. - (didData._1.toW3C(prismDID), didData._2.toW3C(prismDID)) + (didData._1.toW3C, didData._2.toW3C(prismDID)) } } diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/service/DIDService.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/service/DIDService.scala index 9052a7db21..1e532495e2 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/service/DIDService.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/service/DIDService.scala @@ -1,5 +1,6 @@ package io.iohk.atala.castor.core.service +import io.iohk.atala.castor.core.model.ProtoModelHelper import io.iohk.atala.castor.core.model.did.{ CanonicalPrismDID, DIDData, @@ -12,17 +13,16 @@ import io.iohk.atala.castor.core.model.did.{ ScheduledDIDOperationDetail, SignedPrismDIDOperation } -import zio.* -import io.iohk.atala.castor.core.model.ProtoModelHelper +import io.iohk.atala.castor.core.model.error.OperationValidationError import io.iohk.atala.castor.core.model.error.{DIDOperationError, DIDResolutionError} import io.iohk.atala.castor.core.util.DIDOperationValidator -import io.iohk.atala.shared.models.HexString -import io.iohk.atala.prism.protos.{node_api, node_models} import io.iohk.atala.prism.protos.node_api.NodeServiceGrpc.NodeService import io.iohk.atala.prism.protos.node_models.OperationOutput.OperationMaybe - +import io.iohk.atala.prism.protos.{node_api, node_models} +import io.iohk.atala.shared.models.HexString +import java.time.Instant import scala.collection.immutable.ArraySeq -import io.iohk.atala.castor.core.model.error.OperationValidationError +import zio.* trait DIDService { def scheduleOperation(operation: SignedPrismDIDOperation): IO[DIDOperationError, ScheduleDIDOperationOutcome] @@ -108,9 +108,14 @@ private class DIDServiceImpl(didOpValidator: DIDOperationValidator, nodeClient: .flatMap(didData => ZIO.fromEither(didData.toDomain)) .mapError(DIDResolutionError.UnexpectedDLTResult.apply) .map { didData => + val (created, updated) = getMinMaxLedgerTime(didDataProto) val metadata = DIDMetadata( lastOperationHash = ArraySeq.from(result.lastUpdateOperation.toByteArray), - deactivated = didData.internalKeys.isEmpty && didData.publicKeys.isEmpty + canonicalId = + unpublishedDidData.map(_ => canonicalDID), // only shows canonicalId if long-form and published + deactivated = didData.internalKeys.isEmpty && didData.publicKeys.isEmpty, + created = created, + updated = updated ) metadata -> didData } @@ -119,6 +124,17 @@ private class DIDServiceImpl(didOpValidator: DIDOperationValidator, nodeClient: } yield publishedDidData.orElse(unpublishedDidData) } + // FIXME: This doesn't play well detecting timestamp context and revoked service due to + // the response from Node missing the ledger data for those items. + private def getMinMaxLedgerTime(didData: node_models.DIDData): (Option[Instant], Option[Instant]) = { + val ledgerTimes = didData.publicKeys.flatMap(_.addedOn) ++ + didData.publicKeys.flatMap(_.revokedOn) ++ + didData.services.flatMap(_.addedOn) ++ + didData.services.flatMap(_.deletedOn) + val instants = ledgerTimes.flatMap(_.toInstant) + (instants.minOption, instants.maxOption) + } + private def extractUnpublishedDIDData(did: LongFormPrismDID): IO[DIDResolutionError, (DIDMetadata, DIDData)] = { ZIO .fromEither(did.createOperation) @@ -134,7 +150,10 @@ private class DIDServiceImpl(didOpValidator: DIDOperationValidator, nodeClient: val metadata = DIDMetadata( lastOperationHash = ArraySeq.from(did.stateHash.toByteArray), - deactivated = false // unpublished DID cannot be deactivated + canonicalId = None, // unpublished DID must not contain canonicalId + deactivated = false, // unpublished DID cannot be deactivated + created = None, // unpublished DID cannot have timestamp + updated = None // unpublished DID cannot have timestamp ) val didData = DIDData( id = did.asCanonical, diff --git a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/util/DIDOperationValidator.scala b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/util/DIDOperationValidator.scala index 135e327fae..84c095a292 100644 --- a/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/util/DIDOperationValidator.scala +++ b/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/util/DIDOperationValidator.scala @@ -58,6 +58,8 @@ private object CreateOperationValidator extends BaseOperationValidator { _ <- validateServiceEndpointLength(config)(operation, extractServiceEndpoint) _ <- validateServiceTypeLength(config)(operation, extractServiceType) _ <- validateUniqueContext(operation, _.context :: Nil) + _ <- validateContextLength(operation, _.context :: Nil) + _ <- validateContextIsUri(operation, _.context :: Nil) _ <- validateMasterKeyExists(operation) } yield () } @@ -100,6 +102,8 @@ private object UpdateOperationValidator extends BaseOperationValidator { _ <- validateServiceEndpointLength(config)(operation, extractServiceEndpoint) _ <- validateServiceTypeLength(config)(operation, extractServiceType) _ <- validateUniqueContext(operation, extractContexts) + _ <- validateContextLength(operation, extractContexts) + _ <- validateContextIsUri(operation, extractContexts) _ <- validatePreviousOperationHash(operation, _.previousOperationHash) _ <- validateNonEmptyUpdateAction(operation) _ <- validateUpdateServiceNonEmpty(operation) @@ -227,6 +231,34 @@ private trait BaseOperationValidator { else Left(OperationValidationError.InvalidArgument("context is not unique")) } + protected def validateContextIsUri[T <: PrismDIDOperation]( + operation: T, + contextExtractor: ContextExtractor[T] + ): Either[OperationValidationError, Unit] = { + val contexts = contextExtractor(operation) + val nonUriContexts = contexts.flatten.filterNot(UriUtils.isValidUriString) + if (nonUriContexts.isEmpty) Right(()) + else + Left( + OperationValidationError.InvalidArgument( + s"context is not a valid URI: ${nonUriContexts.mkString("[", ", ", "]")}" + ) + ) + } + + protected def validateContextLength[T <: PrismDIDOperation]( + operation: T, + contextExtractor: ContextExtractor[T] + ): Either[OperationValidationError, Unit] = { + val contexts = contextExtractor(operation) + val invalidContexts = contexts.flatten.filter(_.length > 100) // FIXME: confirm this value with the spec + if (invalidContexts.isEmpty) Right(()) + else + Left( + OperationValidationError.InvalidArgument(s"context is too long: ${invalidContexts.mkString("[", ", ", "]")}") + ) + } + protected def validateKeyIdIsUriFragment[T <: PrismDIDOperation]( operation: T, keyIdExtractor: KeyIdExtractor[T] diff --git a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelperSpec.scala b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelperSpec.scala index 8d0a2b336f..f24a54f88f 100644 --- a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelperSpec.scala +++ b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/model/did/w3c/W3CModelHelperSpec.scala @@ -115,7 +115,7 @@ object W3CModelHelperSpec extends ZIOSpecDefault { Seq( "https://www.w3.org/ns/did/v1", "https://identity.foundation/.well-known/did-configuration/v1", - // "https://w3id.org/security/suites/jws-2020/v1", // TODO: enable when align the key type (ATL-3788) + "https://w3id.org/security/suites/jws-2020/v1", "user-defined-context" ) ) @@ -133,7 +133,7 @@ object W3CModelHelperSpec extends ZIOSpecDefault { hasSameElements( Seq( "https://www.w3.org/ns/did/v1", - // "https://w3id.org/security/suites/jws-2020/v1", // TODO: enable when align the key type (ATL-3788) + "https://w3id.org/security/suites/jws-2020/v1", "user-defined-context" ) ) diff --git a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/service/DIDServiceSpec.scala b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/service/DIDServiceSpec.scala index 4f6f376acd..ed5259dd76 100644 --- a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/service/DIDServiceSpec.scala +++ b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/service/DIDServiceSpec.scala @@ -6,6 +6,7 @@ import io.iohk.atala.castor.core.model.error.DIDResolutionError import io.iohk.atala.castor.core.util.{DIDOperationValidator, GenUtils} import io.iohk.atala.prism.protos.common_models.{HealthCheckRequest, HealthCheckResponse} import io.iohk.atala.prism.protos.node_api +import io.iohk.atala.prism.protos.node_models import io.iohk.atala.prism.protos.node_api.{ GetBatchStateRequest, GetBatchStateResponse, @@ -39,6 +40,28 @@ object DIDServiceSpec extends ZIOSpecDefault { lastUpdateOperation = ByteString.EMPTY ) + private def mockNodeService( + createOperation: PrismDIDOperation.Create + ): ULayer[node_api.NodeServiceGrpc.NodeService] = { + import io.iohk.atala.castor.core.model.ProtoModelHelper.* + + val operationProto = createOperation.toProto + val didData = node_models.DIDData( + id = createOperation.did.suffix.toString, + publicKeys = operationProto.value.didData.get.publicKeys, + services = operationProto.value.didData.get.services, + context = operationProto.value.didData.get.context + ) + + mockNodeService( + GetDidDocumentResponse( + document = Some(didData), + lastSyncedBlockTimestamp = None, + lastUpdateOperation = ByteString.EMPTY + ) + ) + } + private def mockNodeService( didData: GetDidDocumentResponse = notFoundDidDocumentResponse ): ULayer[node_api.NodeServiceGrpc.NodeService] = ZLayer.succeed { @@ -77,7 +100,10 @@ object DIDServiceSpec extends ZIOSpecDefault { private def didServiceLayer(): ULayer[DIDService] = DIDOperationValidator.layer() ++ mockNodeService() >>> DIDServiceImpl.layer - override def spec = suite("DIDServiceImpl")(resolveDIDSpec).provide(didServiceLayer()) + private def didServiceLayer(createOperation: PrismDIDOperation.Create): ULayer[DIDService] = + DIDOperationValidator.layer() ++ mockNodeService(createOperation: PrismDIDOperation.Create) >>> DIDServiceImpl.layer + + override def spec = suite("DIDServiceImpl")(resolveDIDSpec.provide(didServiceLayer()), resolveDIDMetadataSpec) private val resolveDIDSpec = suite("resolveDID")( test("long-form unpublished DID returns content in encoded state") { @@ -111,4 +137,33 @@ object DIDServiceSpec extends ZIOSpecDefault { } ) + private val resolveDIDMetadataSpec = suite("resolveDID metadata")( + test("short-form published DID doesn't return canonicalID")( + for { + operation <- GenUtils.createOperation.runCollectN(1).map(_.head) + prismDID = PrismDID.buildLongFormFromOperation(operation) + svc <- ZIO.service[DIDService].provide(didServiceLayer(operation)) + resolutionResult <- svc.resolveDID(prismDID.asCanonical).someOrFailException + (didMetadata, _) = resolutionResult + } yield assert(didMetadata.canonicalId)(isNone) + ), + test("long-form published DID return canonicalID")( + for { + operation <- GenUtils.createOperation.runCollectN(1).map(_.head) + prismDID = PrismDID.buildLongFormFromOperation(operation) + svc <- ZIO.service[DIDService].provide(didServiceLayer(operation)) + resolutionResult <- svc.resolveDID(prismDID).someOrFailException + (didMetadata, _) = resolutionResult + } yield assert(didMetadata.canonicalId)(isSome(equalTo(prismDID.asCanonical))) + ), + test("long-form unpublished DID doesn't return canonicalID")( + for { + operation <- GenUtils.createOperation.runCollectN(1).map(_.head) + prismDID = PrismDID.buildLongFormFromOperation(operation) + svc <- ZIO.service[DIDService].provide(didServiceLayer()) + resolutionResult <- svc.resolveDID(prismDID).someOrFailException + (didMetadata, _) = resolutionResult + } yield assert(didMetadata.canonicalId)(isNone) + ), + ) } diff --git a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/util/DIDOperationValidatorSpec.scala b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/util/DIDOperationValidatorSpec.scala index 2b5e20d3d4..22bc016397 100644 --- a/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/util/DIDOperationValidatorSpec.scala +++ b/castor/lib/core/src/test/scala/io/iohk/atala/castor/core/util/DIDOperationValidatorSpec.scala @@ -259,6 +259,18 @@ object DIDOperationValidatorSpec extends ZIOSpecDefault { invalidArgumentContainsString("context is not unique") ) }, + test("reject CreateOperation when context is not a URI") { + val op = createPrismDIDOperation(context = Seq("not a URI")) + assert(DIDOperationValidator(Config.default).validate(op))( + invalidArgumentContainsString("context is not a valid URI") + ) + }, + test("reject CreateOperation when context is too long") { + val op = createPrismDIDOperation(context = Seq(s"http://example.com/${"0" * 100}")) + assert(DIDOperationValidator(Config.default).validate(op))( + invalidArgumentContainsString("context is too long") + ) + }, test("reject CreateOperation on too long serviceType") { val service = Service( id = "service", @@ -519,6 +531,20 @@ object DIDOperationValidatorSpec extends ZIOSpecDefault { invalidArgumentContainsString("context is not unique") ) }, + test("reject UpdateOperation when context is not a URI") { + val action1 = UpdateDIDAction.PatchContext(Seq("not a URI")) + val op = updatePrismDIDOperation(Seq(action1)) + assert(DIDOperationValidator(Config.default).validate(op))( + invalidArgumentContainsString("context is not a valid URI") + ) + }, + test("reject UpdateOperation when context is too long") { + val action1 = UpdateDIDAction.PatchContext(Seq(s"http://example.com/${"0" * 100}")) + val op = updatePrismDIDOperation(Seq(action1)) + assert(DIDOperationValidator(Config.default).validate(op))( + invalidArgumentContainsString("context is too long") + ) + }, test("reject UpdateOperation on too long serviceType") { val action = UpdateDIDAction.AddService( Service( diff --git a/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/DidResolver.scala b/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/DidResolver.scala index 5e18c2697d..aaf5f5260d 100644 --- a/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/DidResolver.scala +++ b/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/DidResolver.scala @@ -171,8 +171,8 @@ class PrismDidResolver(didService: DIDService) extends DidResolver { JsonWebKey( crv = Some(jwk.crv), kty = jwk.kty, - x = Some(jwk.x), - y = Some(jwk.y) + x = jwk.x, + y = jwk.y ) } diff --git a/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/JWTVerification.scala b/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/JWTVerification.scala index e30ce54fdc..873be46f23 100644 --- a/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/JWTVerification.scala +++ b/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/JWTVerification.scala @@ -20,7 +20,7 @@ object JWTVerification { // JWT algo <-> publicKey type mapping reference // https://github.com/decentralized-identity/did-jwt/blob/8b3655097a1382934cabdf774d580e6731a636b1/src/JWT.ts#L146 val SUPPORT_PUBLIC_KEY_TYPES: Map[String, Set[String]] = Map( - "ES256K" -> Set("EcdsaSecp256k1VerificationKey2019"), + "ES256K" -> Set("EcdsaSecp256k1VerificationKey2019", "JsonWebKey2020"), "ES256" -> Set("ES256") // TODO: Only use valid type (added just for compatibility in the Demo code) ) diff --git a/prism-agent/service/api/http/prism-agent-openapi-spec.yaml b/prism-agent/service/api/http/prism-agent-openapi-spec.yaml index 26fe57a603..0733c98781 100644 --- a/prism-agent/service/api/http/prism-agent-openapi-spec.yaml +++ b/prism-agent/service/api/http/prism-agent-openapi-spec.yaml @@ -8,8 +8,9 @@ paths: tags: - Schema Registry summary: Lookup schemas by indexed fields - description: 'Lookup schemas by `author`, `name`, `tags` parameters and control - the pagination by `offset` and `limit` parameters ' + description: + "Lookup schemas by `author`, `name`, `tags` parameters and control + the pagination by `offset` and `limit` parameters " operationId: lookupSchemasByQuery parameters: - name: author @@ -54,29 +55,30 @@ paths: schema: type: string responses: - '200': + "200": description: Collection of CredentialSchema records. content: application/json: schema: - $ref: '#/components/schemas/CredentialSchemaResponsePage' - '400': + $ref: "#/components/schemas/CredentialSchemaResponsePage" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" post: tags: - Schema Registry summary: Publish new schema to the schema registry - description: Create the new credential schema record with metadata and internal + description: + Create the new credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it. operationId: createSchema @@ -85,33 +87,34 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CredentialSchemaInput' + $ref: "#/components/schemas/CredentialSchemaInput" required: true responses: - '201': + "201": description: The new credential schema record is successfully created content: application/json: schema: - $ref: '#/components/schemas/CredentialSchemaResponse' - '400': + $ref: "#/components/schemas/CredentialSchemaResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /schema-registry/{author}/{id}: put: tags: - Schema Registry summary: Publish the new version of the credential schema to the schema registry - description: Publish the new version of the credential schema record with metadata + description: + Publish the new version of the credential schema record with metadata and internal JSON Schema on behalf of Cloud Agent. The credential schema will be signed by the keys of Cloud Agent and issued by the DID that corresponds to it. @@ -119,14 +122,16 @@ paths: parameters: - name: author in: path - description: DID of the identity which authored the credential schema. A piece + description: + DID of the identity which authored the credential schema. A piece of Metadata. required: true schema: type: string - name: id in: path - description: A locally unique identifier to address the schema. UUID is generated + description: + A locally unique identifier to address the schema. UUID is generated by the backend. required: true schema: @@ -137,27 +142,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CredentialSchemaInput' + $ref: "#/components/schemas/CredentialSchemaInput" required: true responses: - '200': + "200": description: The credential schema record is successfully updated content: application/json: schema: - $ref: '#/components/schemas/CredentialSchemaResponse' - '400': + $ref: "#/components/schemas/CredentialSchemaResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /schema-registry/schemas/{guid}: get: tags: @@ -174,30 +179,30 @@ paths: type: string format: uuid responses: - '200': + "200": description: CredentialSchema found by `guid` content: application/json: schema: - $ref: '#/components/schemas/CredentialSchemaResponse' - '400': + $ref: "#/components/schemas/CredentialSchemaResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /schema-registry/test: get: tags: @@ -206,13 +211,13 @@ paths: description: Trace the request input from the point of view of the server operationId: test responses: - '200': - description: '' + "200": + description: "" content: application/json: schema: type: string - '400': + "400": description: Invalid value content: text/plain: @@ -223,7 +228,8 @@ paths: tags: - Verification summary: Lookup verification policies by query - description: Lookup verification policies by `name`, and control the pagination + description: + Lookup verification policies by `name`, and control the pagination by `offset` and `limit` parameters operationId: lookupVerificationPoliciesByQuery parameters: @@ -250,24 +256,24 @@ paths: schema: type: string responses: - '200': - description: '' + "200": + description: "" content: application/json: schema: - $ref: '#/components/schemas/VerificationPolicyPage' - '400': + $ref: "#/components/schemas/VerificationPolicyPage" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" post: tags: - Verification @@ -279,27 +285,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/VerificationPolicyInput' + $ref: "#/components/schemas/VerificationPolicyInput" required: true responses: - '201': + "201": description: Created verification policy entity content: application/json: schema: - $ref: '#/components/schemas/VerificationPolicy' - '400': + $ref: "#/components/schemas/VerificationPolicy" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /verification/policies/{id}: get: tags: @@ -316,30 +322,30 @@ paths: type: string format: uuid responses: - '200': - description: '' + "200": + description: "" content: application/json: schema: - $ref: '#/components/schemas/VerificationPolicy' - '400': + $ref: "#/components/schemas/VerificationPolicy" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" put: tags: - Verification @@ -365,33 +371,33 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/VerificationPolicyInput' + $ref: "#/components/schemas/VerificationPolicyInput" required: true responses: - '200': - description: '' + "200": + description: "" content: application/json: schema: - $ref: '#/components/schemas/VerificationPolicy' - '400': + $ref: "#/components/schemas/VerificationPolicy" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" delete: tags: - Verification @@ -414,26 +420,26 @@ paths: type: integer format: int32 responses: - '200': + "200": description: Verification policy deleted successfully - '400': + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /connections: get: tags: @@ -455,24 +461,24 @@ paths: type: integer format: int32 responses: - '200': + "200": description: The list of connection records. content: application/json: schema: - $ref: '#/components/schemas/ConnectionsPage' - '400': + $ref: "#/components/schemas/ConnectionsPage" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" post: tags: - Connections Management @@ -488,28 +494,29 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateConnectionRequest' + $ref: "#/components/schemas/CreateConnectionRequest" required: true responses: - '201': - description: The connection record was created successfully, and is returned + "201": + description: + The connection record was created successfully, and is returned in the response body. content: application/json: schema: - $ref: '#/components/schemas/Connection' - '400': + $ref: "#/components/schemas/Connection" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /connections/{connectionId}: get: tags: @@ -526,30 +533,30 @@ paths: type: string format: uuid responses: - '200': + "200": description: The connection record. content: application/json: schema: - $ref: '#/components/schemas/Connection' - '400': + $ref: "#/components/schemas/Connection" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /connection-invitations: post: tags: @@ -562,32 +569,33 @@ paths: It returns a connection object in `ConnectionRequestPending` state, until the Connection Request is eventually sent to the inviter by the prism-agent's background process. The connection object state will then automatically move to `ConnectionRequestSent`. operationId: acceptConnectionInvitation requestBody: - description: The request used by an invitee to accept a connection invitation + description: + The request used by an invitee to accept a connection invitation received from an inviter, using out-of-band mechanism. content: application/json: schema: - $ref: '#/components/schemas/AcceptConnectionInvitationRequest' + $ref: "#/components/schemas/AcceptConnectionInvitationRequest" required: true responses: - '200': + "200": description: The invitation was successfully accepted. content: application/json: schema: - $ref: '#/components/schemas/Connection' - '400': + $ref: "#/components/schemas/Connection" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /dids/{didRef}: get: tags: @@ -608,69 +616,69 @@ paths: type: string example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff responses: - '200': + "200": description: The resolution result or W3C DID document representation content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' - '400': + $ref: "#/components/schemas/DIDDocument" + "400": description: Invalid DID or DID URL content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' - '404': + $ref: "#/components/schemas/DIDDocument" + "404": description: The DID is not found content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' - '406': + $ref: "#/components/schemas/DIDDocument" + "406": description: The DID document representation is not supported content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' - '410': + $ref: "#/components/schemas/DIDDocument" + "410": description: The DID is deactivated content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' - '500': + $ref: "#/components/schemas/DIDDocument" + "500": description: Internal error content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' - '501': + $ref: "#/components/schemas/DIDDocument" + "501": description: The DID method is not supported content: application/ld+json; profile=https://w3id.org/did-resolution: schema: - $ref: '#/components/schemas/DIDResolutionResult' + $ref: "#/components/schemas/DIDResolutionResult" application/did+ld+json: schema: - $ref: '#/components/schemas/DIDDocument' + $ref: "#/components/schemas/DIDDocument" /did-registrar/dids: get: tags: @@ -695,24 +703,24 @@ paths: type: integer format: int32 responses: - '200': + "200": description: List Prism Agent managed DIDs content: application/json: schema: - $ref: '#/components/schemas/ManagedDIDPage' - '400': + $ref: "#/components/schemas/ManagedDIDPage" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" post: tags: - DID Registrar @@ -725,33 +733,33 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateManagedDidRequest' + $ref: "#/components/schemas/CreateManagedDidRequest" required: true responses: - '201': + "201": description: Created unpublished DID. content: application/json: schema: - $ref: '#/components/schemas/CreateManagedDIDResponse' - '400': + $ref: "#/components/schemas/CreateManagedDIDResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '422': + $ref: "#/components/schemas/ErrorResponse" + "422": description: Unable to process the request content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /did-registrar/dids/{didRef}: get: tags: @@ -768,30 +776,30 @@ paths: type: string example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff responses: - '200': + "200": description: Get Prism Agent managed DID content: application/json: schema: - $ref: '#/components/schemas/ManagedDID' - '400': + $ref: "#/components/schemas/ManagedDID" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /did-registrar/dids/{didRef}/publications: post: tags: @@ -808,35 +816,36 @@ paths: type: string example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff responses: - '202': + "202": description: Publishing DID to the VDR. content: application/json: schema: - $ref: '#/components/schemas/DIDOperationResponse' - '400': + $ref: "#/components/schemas/DIDOperationResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /did-registrar/dids/{didRef}/updates: post: tags: - DID Registrar - summary: Update DID in Prism Agent's wallet and post update operation to the + summary: + Update DID in Prism Agent's wallet and post update operation to the VDR description: |- Update DID in Prism Agent's wallet and post update operation to the VDR. @@ -856,52 +865,54 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateManagedDIDRequest' + $ref: "#/components/schemas/UpdateManagedDIDRequest" required: true responses: - '202': + "202": description: DID update operation accepted content: application/json: schema: - $ref: '#/components/schemas/DIDOperationResponse' - '400': + $ref: "#/components/schemas/DIDOperationResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '409': + $ref: "#/components/schemas/ErrorResponse" + "409": description: Cannot process due to conflict with current state of the resource content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '422': + $ref: "#/components/schemas/ErrorResponse" + "422": description: Unable to process the request content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /did-registrar/dids/{didRef}/deactivations: post: tags: - DID Registrar - summary: Deactivate DID in Prism Agent's wallet and post deactivate operation + summary: + Deactivate DID in Prism Agent's wallet and post deactivate operation to the VDR - description: Deactivate DID in Prism Agent's wallet and post deactivate operation + description: + Deactivate DID in Prism Agent's wallet and post deactivate operation to the VDR. operationId: postDid-registrarDidsDidrefDeactivations parameters: @@ -913,41 +924,42 @@ paths: type: string example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff responses: - '202': + "202": description: DID deactivation operation accepted content: application/json: schema: - $ref: '#/components/schemas/DIDOperationResponse' - '400': + $ref: "#/components/schemas/DIDOperationResponse" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '422': + $ref: "#/components/schemas/ErrorResponse" + "422": description: Unable to process the request content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /issue-credentials/credential-offers: post: tags: - Issue Credentials Protocol - summary: As a credential issuer, create a new credential offer to be sent to + summary: + As a credential issuer, create a new credential offer to be sent to a holder. description: Creates a new credential offer in the database operationId: createCredentialOffer @@ -956,27 +968,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateIssueCredentialRecordRequest' + $ref: "#/components/schemas/CreateIssueCredentialRecordRequest" required: true responses: - '201': + "201": description: The issue credential record. content: application/json: schema: - $ref: '#/components/schemas/IssueCredentialRecord' - '400': + $ref: "#/components/schemas/IssueCredentialRecord" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /issue-credentials/records: get: tags: @@ -1004,24 +1016,24 @@ paths: schema: type: string responses: - '200': + "200": description: The list of issue credential records. content: application/json: schema: - $ref: '#/components/schemas/IssueCredentialRecordPage' - '400': + $ref: "#/components/schemas/IssueCredentialRecordPage" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /issue-credentials/records/{recordId}: get: tags: @@ -1037,36 +1049,37 @@ paths: schema: type: string responses: - '200': + "200": description: The issue credential record. content: application/json: schema: - $ref: '#/components/schemas/IssueCredentialRecord' - '400': + $ref: "#/components/schemas/IssueCredentialRecord" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /issue-credentials/records/{recordId}/accept-offer: post: tags: - Issue Credentials Protocol summary: As a holder, accepts a credential offer received from an issuer. - description: Accepts a credential offer received from a VC issuer and sends + description: + Accepts a credential offer received from a VC issuer and sends back a credential request. operationId: acceptCredentialOffer parameters: @@ -1081,40 +1094,42 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AcceptCredentialOfferRequest' + $ref: "#/components/schemas/AcceptCredentialOfferRequest" required: true responses: - '200': + "200": description: The issue credential offer was successfully accepted. content: application/json: schema: - $ref: '#/components/schemas/IssueCredentialRecord' - '400': + $ref: "#/components/schemas/IssueCredentialRecord" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /issue-credentials/records/{recordId}/issue-credential: post: tags: - Issue Credentials Protocol - summary: As an issuer, issues the verifiable credential related to the specified + summary: + As an issuer, issues the verifiable credential related to the specified record. - description: Sends credential to a holder (holder DID is specified in credential + description: + Sends credential to a holder (holder DID is specified in credential as subjectDid). Credential is constructed from the credential records found by credential id. operationId: issueCredential @@ -1126,31 +1141,32 @@ paths: schema: type: string responses: - '200': - description: The request was processed successfully and the credential will + "200": + description: + The request was processed successfully and the credential will be issued asynchronously. content: application/json: schema: - $ref: '#/components/schemas/IssueCredentialRecord' - '400': + $ref: "#/components/schemas/IssueCredentialRecord" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /present-proof/presentations: get: tags: @@ -1177,30 +1193,32 @@ paths: schema: type: string responses: - '200': + "200": description: The list of proof presentation records. content: application/json: schema: - $ref: '#/components/schemas/PresentationStatusPage' - '400': + $ref: "#/components/schemas/PresentationStatusPage" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" post: tags: - Present Proof - summary: As a Verifier, create a new proof presentation request and send it + summary: + As a Verifier, create a new proof presentation request and send it to the Prover. - description: Holder presents proof derived from the verifiable credential to + description: + Holder presents proof derived from the verifiable credential to verifier. operationId: requestPresentation requestBody: @@ -1208,33 +1226,35 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RequestPresentationInput' + $ref: "#/components/schemas/RequestPresentationInput" required: true responses: - '201': - description: The proof presentation request was created successfully and + "201": + description: + The proof presentation request was created successfully and will be sent asynchronously to the Prover. content: application/json: schema: - $ref: '#/components/schemas/RequestPresentationOutput' - '400': + $ref: "#/components/schemas/RequestPresentationOutput" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" /present-proof/presentations/{presentationId}: get: tags: - Present Proof - summary: Gets an existing proof presentation record by its unique identifier. + summary: + Gets an existing proof presentation record by its unique identifier. More information on the error can be found in the response body. description: Returns an existing presentation record by id. operationId: getPresentation @@ -1247,34 +1267,35 @@ paths: type: string format: uuid responses: - '200': + "200": description: The proof presentation record. content: application/json: schema: - $ref: '#/components/schemas/PresentationStatus' - '400': + $ref: "#/components/schemas/PresentationStatus" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" patch: tags: - Present Proof - summary: Updates the proof presentation record matching the unique identifier, + summary: + Updates the proof presentation record matching the unique identifier, with the specific action to perform. description: Accept or reject presentation of proof request. operationId: updatePresentation @@ -1291,33 +1312,33 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RequestPresentationAction' + $ref: "#/components/schemas/RequestPresentationAction" required: true responses: - '200': + "200": description: The proof presentation record was successfully updated. content: application/json: schema: - $ref: '#/components/schemas/PresentationStatus' - '400': + $ref: "#/components/schemas/PresentationStatus" + "400": description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '404': + $ref: "#/components/schemas/ErrorResponse" + "404": description: Resource could not be found content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' - '500': + $ref: "#/components/schemas/ErrorResponse" + "500": description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/ErrorResponse' + $ref: "#/components/schemas/ErrorResponse" components: schemas: AcceptConnectionInvitationRequest: @@ -1336,7 +1357,8 @@ components: properties: subjectId: type: string - description: The short-form subject Prism DID to which the verifiable credential + description: + The short-form subject Prism DID to which the verifiable credential should be issued. example: did:prism:3bb0505d13fcb04d28a48234edb27b0d4e6d7e18a81e2c1abab58f3bbc21ce6f ActionType: @@ -1369,12 +1391,14 @@ components: example: Peter myDid: type: string - description: The DID representing me as the inviter or invitee in this specific + description: + The DID representing me as the inviter or invitee in this specific connection. example: did:peer:12345 theirDid: type: string - description: The DID representing the other peer as the an inviter or invitee + description: + The DID representing the other peer as the an inviter or invitee in this specific connection. example: did:peer:67890 role: @@ -1401,7 +1425,7 @@ components: - ProblemReportSent - ProblemReportReceived invitation: - $ref: '#/components/schemas/ConnectionInvitation' + $ref: "#/components/schemas/ConnectionInvitation" createdAt: type: string description: The date and time the connection record was created. @@ -1430,23 +1454,27 @@ components: properties: id: type: string - description: The unique identifier of the invitation. It should be used + description: + The unique identifier of the invitation. It should be used as parent thread ID (pthid) for the Connection Request message that follows. format: uuid example: 0527aea1-d131-3948-a34d-03af39aba8b4 type: type: string - description: The DIDComm Message Type URI (MTURI) the invitation message + description: + The DIDComm Message Type URI (MTURI) the invitation message complies with. example: https://didcomm.org/out-of-band/2.0/invitation from: type: string - description: The DID representing the sender to be used by recipients for + description: + The DID representing the sender to be used by recipients for future interactions. example: did:peer:1234457 invitationUrl: type: string - description: The invitation message encoded as a URL. This URL follows the + description: + The invitation message encoded as a URL. This URL follows the Out of [Band 2.0 protocol](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) and can be used to generate a QR code for example. example: https://my.domain.com/path?_oob=eyJAaWQiOiIzZmE4NWY2NC01NzE3LTQ1NjItYjNmYy0yYzk2M2Y2NmFmYTYiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvbXktZmFtaWx5LzEuMC9teS1tZXNzYWdlLXR5cGUiLCJkaWQiOiJXZ1d4cXp0ck5vb0c5MlJYdnhTVFd2IiwiaW1hZ2VVcmwiOiJodHRwOi8vMTkyLjE2OC41Ni4xMDEvaW1nL2xvZ28uanBnIiwibGFiZWwiOiJCb2IiLCJyZWNpcGllbnRLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInJvdXRpbmdLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8xOTIuMTY4LjU2LjEwMTo4MDIwIn0= @@ -1461,28 +1489,28 @@ components: contents: type: array items: - $ref: '#/components/schemas/Connection' - description: '' + $ref: "#/components/schemas/Connection" + description: "" examples: [] kind: type: string - description: '' + description: "" example: ConnectionsPage self: type: string - description: '' + description: "" example: /prism-agent/connections?offset=10&limit=10 pageOf: type: string - description: '' - example: '' + description: "" + example: "" next: type: string - description: '' + description: "" example: /prism-agent/connections?offset=20&limit=10 previous: type: string - description: '' + description: "" example: /prism-agent/connections?offset=0&limit=10 CreateConnectionRequest: type: object @@ -1500,15 +1528,17 @@ components: properties: validityPeriod: type: number - description: The validity period in seconds of the verifiable credential + description: + The validity period in seconds of the verifiable credential that will be issued. format: double example: 3600.0 claims: - $ref: '#/components/schemas/Map_String' + $ref: "#/components/schemas/Map_String" automaticIssuance: type: boolean - description: Specifies whether or not the credential should be automatically + description: + Specifies whether or not the credential should be automatically generated and issued when receiving the `CredentialRequest` from the holder. If set to `false`, a manual approval by the issuer via API call will be required for the VC to be issued. @@ -1519,7 +1549,8 @@ components: example: did:prism:issuerofverifiablecredentials connectionId: type: string - description: The unique identifier of a DIDComm connection that already + description: + The unique identifier of a DIDComm connection that already exists between the issuer and the holder, and that will be used to execute the issue credential protocol. CreateManagedDIDResponse: @@ -1537,18 +1568,18 @@ components: type: object properties: documentTemplate: - $ref: '#/components/schemas/CreateManagedDidRequestDocumentTemplate' + $ref: "#/components/schemas/CreateManagedDidRequestDocumentTemplate" CreateManagedDidRequestDocumentTemplate: type: object properties: publicKeys: type: array items: - $ref: '#/components/schemas/ManagedDIDKeyTemplate' + $ref: "#/components/schemas/ManagedDIDKeyTemplate" services: type: array items: - $ref: '#/components/schemas/Service' + $ref: "#/components/schemas/Service" CredentialSchemaInput: required: - name @@ -1560,13 +1591,15 @@ components: properties: name: type: string - description: A human-readable name for the credential schema. A piece of + description: + A human-readable name for the credential schema. A piece of Metadata. example: DrivingLicense minLength: 1 version: type: string - description: Denotes the revision of a given Credential Schema. It should + description: + Denotes the revision of a given Credential Schema. It should follow semantic version convention to describe the impact of the schema evolution. example: 1.0.0 @@ -1578,11 +1611,13 @@ components: minLength: 1 type: type: string - description: This field resolves to a JSON schema with details about the + description: + This field resolves to a JSON schema with details about the schema metadata that applies to the schema. A piece of Metadata. example: https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json schema: - description: Valid JSON Schema where the Credential Schema data fields are + description: + Valid JSON Schema where the Credential Schema data fields are defined. A piece of Metadata example: $id: driving-license-1.0 @@ -1617,7 +1652,8 @@ components: type: array items: type: string - description: Tokens that allow to lookup and filter the credential schema + description: + Tokens that allow to lookup and filter the credential schema records. examples: - driving @@ -1625,7 +1661,8 @@ components: - id author: type: string - description: DID of the identity which authored the credential schema. A + description: + DID of the identity which authored the credential schema. A piece of Metadata. example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff pattern: ^did:(?[a-z0-9]+(:[a-z0-9]+)*)\:(?[^#?]*)$ @@ -1646,29 +1683,34 @@ components: properties: guid: type: string - description: 'Globally unique id of the credential schema.It''s composed + description: + "Globally unique id of the credential schema.It's composed from the bytes of the string that contain the `author`, `name`, and `version` - values.The string format looks like the resource identifier: `author`/`id`?version=`version`' + values.The string format looks like the resource identifier: `author`/`id`?version=`version`" format: uuid example: 0527aea1-d131-3948-a34d-03af39aba8b4 id: type: string - description: A locally unique identifier to address the schema. UUID is + description: + A locally unique identifier to address the schema. UUID is generated by the backend. example: 0527aea1-d131-3948-a34d-03af39aba8b5 longId: type: string - description: Resource id of the credential schema. Contains the `author`'s + description: + Resource id of the credential schema. Contains the `author`'s DID, `id` and `version` fields. example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff/0527aea1-d131-3948-a34d-03af39aba8b4?version=1.0.0 name: type: string - description: A human-readable name for the credential schema. A piece of + description: + A human-readable name for the credential schema. A piece of Metadata. example: DrivingLicense version: type: string - description: Denotes the revision of a given Credential Schema. It should + description: + Denotes the revision of a given Credential Schema. It should follow semantic version convention to describe the impact of the schema evolution. example: 1.0.0 @@ -1676,7 +1718,8 @@ components: type: array items: type: string - description: Tokens that allow to lookup and filter the credential schema + description: + Tokens that allow to lookup and filter the credential schema records. examples: - driving @@ -1688,11 +1731,13 @@ components: example: Simple credential schema for the driving licence verifiable credential. type: type: string - description: This field resolves to a JSON schema with details about the + description: + This field resolves to a JSON schema with details about the schema metadata that applies to the schema. A piece of Metadata. example: https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json schema: - description: Valid JSON Schema where the Credential Schema data fields are + description: + Valid JSON Schema where the Credential Schema data fields are defined. A piece of Metadata example: $id: driving-license-1.0 @@ -1725,25 +1770,29 @@ components: additionalProperties: true author: type: string - description: DID of the identity which authored the credential schema. A + description: + DID of the identity which authored the credential schema. A piece of Metadata. example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff authored: type: string - description: '[RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date on - which the credential schema was created. A piece of Metadata.' + description: + "[RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date on + which the credential schema was created. A piece of Metadata." format: date-time example: 2022-03-10T12:00Z proof: - $ref: '#/components/schemas/Proof' + $ref: "#/components/schemas/Proof" kind: type: string - description: A string that identifies the type of resource being returned + description: + A string that identifies the type of resource being returned in the response. example: CredentialSchema self: type: string - description: The URL that uniquely identifies the resource being returned + description: + The URL that uniquely identifies the resource being returned in the response. example: /prism-agent/schema-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 CredentialSchemaResponsePage: @@ -1756,13 +1805,15 @@ components: contents: type: array items: - $ref: '#/components/schemas/CredentialSchemaResponse' - description: A sequence of CredentialSchemaResponse objects representing + $ref: "#/components/schemas/CredentialSchemaResponse" + description: + A sequence of CredentialSchemaResponse objects representing the list of credential schemas that the API response contains examples: [] kind: type: string - description: A string field indicating the type of the API response. In + description: + A string field indicating the type of the API response. In this case, it will always be set to `CredentialSchemaPage` example: CredentialSchemaPage self: @@ -1771,18 +1822,21 @@ components: example: /prism-agent/schema-registry/schemas?skip=10&limit=10 pageOf: type: string - description: A string field indicating the type of resource that the contents + description: + A string field indicating the type of resource that the contents field contains example: /prism-agent/schema-registry/schemas next: type: string - description: An optional string field containing the URL of the next page + description: + An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. example: /prism-agent/schema-registry/schemas?skip=20&limit=10 previous: type: string - description: An optional string field containing the URL of the previous + description: + An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. example: /prism-agent/schema-registry/schemas?skip=0&limit=10 @@ -1791,7 +1845,7 @@ components: - id type: object properties: - '@context': + "@context": type: array items: type: string @@ -1804,12 +1858,12 @@ components: example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff controller: type: string - description: '[DID controller](https://www.w3.org/TR/did-core/#did-controller)' + description: "[DID controller](https://www.w3.org/TR/did-core/#did-controller)" example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff verificationMethod: type: array items: - $ref: '#/components/schemas/VerificationMethod' + $ref: "#/components/schemas/VerificationMethod" authentication: type: array items: @@ -1833,14 +1887,15 @@ components: service: type: array items: - $ref: '#/components/schemas/Service' + $ref: "#/components/schemas/Service" description: A W3C compliant Prism DID document representation. DIDDocumentMetadata: type: object properties: deactivated: type: boolean - description: If a DID has been deactivated, DID document metadata MUST include + description: + If a DID has been deactivated, DID document metadata MUST include this property with the boolean value true. If a DID has not been deactivated, this property is OPTIONAL, but if included, MUST have the boolean value false. @@ -1853,14 +1908,14 @@ components: If a DID is in long form and has been published, DID document metadata MUST contain a `canonicalId`` property with the short form DID as its value. If a DID in short form or has not been published, DID document metadata MUST NOT contain a `canonicalId` property. example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff - description: '[DID document metadata](https://www.w3.org/TR/did-core/#did-document-metadata)' + description: "[DID document metadata](https://www.w3.org/TR/did-core/#did-document-metadata)" DIDOperationResponse: required: - scheduledOperation type: object properties: scheduledOperation: - $ref: '#/components/schemas/DidOperationSubmission' + $ref: "#/components/schemas/DidOperationSubmission" DIDResolutionMetadata: type: object properties: @@ -1876,24 +1931,24 @@ components: type: string description: The media type of the returned DID document example: application/did+ld+json - description: '[DID resolution metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata)' + description: "[DID resolution metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata)" DIDResolutionResult: required: - - '@context' + - "@context" - didDocumentMetadata - didResolutionMetadata type: object properties: - '@context': + "@context": type: string description: The JSON-LD context for the DID resolution result. example: https://w3id.org/did-resolution/v1 didDocument: - $ref: '#/components/schemas/DIDDocument' + $ref: "#/components/schemas/DIDDocument" didDocumentMetadata: - $ref: '#/components/schemas/DIDDocumentMetadata' + $ref: "#/components/schemas/DIDDocumentMetadata" didResolutionMetadata: - $ref: '#/components/schemas/DIDResolutionMetadata' + $ref: "#/components/schemas/DIDResolutionMetadata" DidOperationSubmission: required: - id @@ -1927,17 +1982,20 @@ components: example: https://example.org/doc/#model-MalformedEmail/ title: type: string - description: A short, human-readable summary of the problem type. It does + description: + A short, human-readable summary of the problem type. It does not change from occurrence to occurrence of the problem. example: Malformed email detail: type: string - description: A human-readable explanation specific to this occurrence of + description: + A human-readable explanation specific to this occurrence of the problem. example: The received '{}à!è@!.b}' email does not conform to the email format instance: type: string - description: A URI reference that identifies the specific occurrence of + description: + A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. example: The received '{}à!è@!.b}' email does not conform to the email format IssueCredentialRecord: @@ -1951,20 +2009,23 @@ components: properties: subjectId: type: string - description: The identifier (e.g DID) of the subject to which the verifiable + description: + The identifier (e.g DID) of the subject to which the verifiable credential will be issued. example: did:prism:subjectofverifiablecredentials validityPeriod: type: number - description: The validity period in seconds of the verifiable credential + description: + The validity period in seconds of the verifiable credential that will be issued. format: double example: 3600.0 claims: - $ref: '#/components/schemas/Map_String' + $ref: "#/components/schemas/Map_String" automaticIssuance: type: boolean - description: Specifies whether or not the credential should be automatically + description: + Specifies whether or not the credential should be automatically generated and issued when receiving the `CredentialRequest` from the holder. If set to `false`, a manual approval by the issuer via API call will be required for the VC to be issued. @@ -1977,15 +2038,17 @@ components: type: string description: The date and time when the issue credential record was created. format: date-time - example: '2023-05-15T13:11:32.328823Z' + example: "2023-05-15T13:11:32.328823Z" updatedAt: type: string - description: The date and time when the issue credential record was last + description: + The date and time when the issue credential record was last updated. format: date-time role: type: string - description: The role played by the Prism agent in the credential issuance + description: + The role played by the Prism agent in the credential issuance flow. example: Issuer protocolState: @@ -2009,7 +2072,8 @@ components: - ProblemReportReceived jwtCredential: type: string - description: The base64-encoded JWT verifiable credential that has been + description: + The base64-encoded JWT verifiable credential that has been sent by the issuer. issuingDID: type: string @@ -2032,26 +2096,30 @@ components: example: /prism-agent/schema-registry/schemas?skip=10&limit=10 pageOf: type: string - description: A string field indicating the type of resource that the contents + description: + A string field indicating the type of resource that the contents field contains example: /prism-agent/schema-registry/schemas next: type: string - description: An optional string field containing the URL of the next page + description: + An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. example: /prism-agent/schema-registry/schemas?skip=20&limit=10 previous: type: string - description: An optional string field containing the URL of the previous + description: + An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. example: /prism-agent/schema-registry/schemas?skip=0&limit=10 contents: type: array items: - $ref: '#/components/schemas/IssueCredentialRecord' - description: A sequence of IssueCredentialRecord objects representing the + $ref: "#/components/schemas/IssueCredentialRecord" + description: + A sequence of IssueCredentialRecord objects representing the list of credential records that the API response contains examples: [] ManagedDID: @@ -2066,7 +2134,8 @@ components: example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff longFormDid: type: string - description: A long-form DID. Mandatory when status is not PUBLISHED and + description: + A long-form DID. Mandatory when status is not PUBLISHED and optional when status is PUBLISHED example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff:Cr4BCrsBElsKBmF1dGgtMRAEQk8KCXNlY3AyNTZrMRIg0opTuxu-zt6aRbT1tPniG4eu4CYsQPM3rrLzvzNiNgwaIIFTnyT2N4U7qCQ78qtWC3-p0el6Hvv8qxG5uuEw-WgMElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIKhBU0eCOO6Vinz_8vhtFSAhYYqrkEXC8PHGxkuIUev8GiAydFHLXb7c22A1Uj_PR21NZp6BCDQqNq2xd244txRgsQ status: @@ -2086,7 +2155,7 @@ components: description: Identifier of a verification material in the DID Document example: key-1 purpose: - $ref: '#/components/schemas/Purpose' + $ref: "#/components/schemas/Purpose" description: key-pair template to add to DID document. ManagedDIDPage: required: @@ -2108,7 +2177,7 @@ components: contents: type: array items: - $ref: '#/components/schemas/ManagedDID' + $ref: "#/components/schemas/ManagedDID" Map_String: type: object description: The claims that will be associated with the issued verifiable credential. @@ -2129,7 +2198,8 @@ components: type: string description: The domain value can be any string or URI. example: https://example-verifier.com - description: The options to use when creating the proof presentation request + description: + The options to use when creating the proof presentation request (e.g., domain, challenge). PresentationStatus: required: @@ -2164,8 +2234,9 @@ components: proofs: type: array items: - $ref: '#/components/schemas/ProofRequestAux' - description: The type of proofs requested in the context of this proof presentation + $ref: "#/components/schemas/ProofRequestAux" + description: + The type of proofs requested in the context of this proof presentation request (e.g., VC schema, trusted issuers, etc.) examples: [] data: @@ -2176,7 +2247,8 @@ components: examples: [] connectionId: type: string - description: The unique identifier of an established connection between + description: + The unique identifier of an established connection between the verifier and the prover. example: bc528dc8-69f1-4c5a-a508-5f8019047900 PresentationStatusPage: @@ -2189,7 +2261,7 @@ components: contents: type: array items: - $ref: '#/components/schemas/PresentationStatus' + $ref: "#/components/schemas/PresentationStatus" description: A sequence of Presentation objects. examples: [] self: @@ -2207,11 +2279,11 @@ components: next: type: string description: URL of the next page (if available) - example: '' + example: "" previous: type: string description: URL of the previous page (if available) - example: '' + example: "" Proof: required: - type @@ -2224,31 +2296,36 @@ components: properties: type: type: string - description: The type of cryptographic signature algorithm used to generate + description: + The type of cryptographic signature algorithm used to generate the proof. example: Ed25519Signature2018 created: type: string - description: The date and time at which the proof was created, in UTC format. + description: + The date and time at which the proof was created, in UTC format. This field is used to ensure that the proof was generated before or at the same time as the credential schema itself. format: date-time example: 2022-03-10T12:00Z verificationMethod: type: string - description: The verification method used to generate the proof. This is + description: + The verification method used to generate the proof. This is usually a DID and key ID combination that can be used to look up the public key needed to verify the proof. example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 proofPurpose: type: string - description: 'The purpose of the proof (for example: `assertionMethod`). + description: + "The purpose of the proof (for example: `assertionMethod`). This indicates that the proof is being used to assert that the issuer - really issued this credential schema instance.' + really issued this credential schema instance." example: assertionMethod proofValue: type: string - description: The cryptographic signature value that was generated using + description: + The cryptographic signature value that was generated using the private key associated with the verification method, and which can be used to verify the proof. example: FiPfjknHikKmZ... @@ -2258,14 +2335,16 @@ components: example: eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik... domain: type: string - description: It specifies the domain context within which the credential + description: + It specifies the domain context within which the credential schema and proof are being used example: prims.atala.com - description: A digital signature over the Credential Schema for the sake of + description: + A digital signature over the Credential Schema for the sake of asserting authorship. A piece of Metadata. example: type: Ed25519Signature2018 - created: '2022-03-10T12:00:00Z' + created: "2022-03-10T12:00:00Z" verificationMethod: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 proofPurpose: assertionMethod proofValue: FiPfjknHikKmZ... @@ -2284,7 +2363,8 @@ components: type: array items: type: string - description: One or more issuers that are trusted by the verifier emitting + description: + One or more issuers that are trusted by the verifier emitting the proof presentation request. examples: - did:web:atalaprism.io/users/testUser @@ -2338,7 +2418,8 @@ components: type: array items: type: string - description: The unique identifier of the issue credential record - and + description: + The unique identifier of the issue credential record - and hence VC - to use as the prover accepts the presentation request. Only applicable on the prover side when the action is `request-accept`. RequestPresentationInput: @@ -2348,16 +2429,18 @@ components: properties: connectionId: type: string - description: The unique identifier of an established connection between + description: + The unique identifier of an established connection between the verifier and the prover. example: bc528dc8-69f1-4c5a-a508-5f8019047900 options: - $ref: '#/components/schemas/Options' + $ref: "#/components/schemas/Options" proofs: type: array items: - $ref: '#/components/schemas/ProofRequestAux' - description: The type of proofs requested in the context of this proof presentation + $ref: "#/components/schemas/ProofRequestAux" + description: + The type of proofs requested in the context of this proof presentation request (e.g., VC schema, trusted issuers, etc.) examples: [] RequestPresentationOutput: @@ -2385,7 +2468,8 @@ components: example: service-1 type: type: string - description: Service type. Can contain multiple possible values as described + description: + Service type. Can contain multiple possible values as described in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) under the construction section. example: LinkedDomains @@ -2393,7 +2477,8 @@ components: type: array items: type: string - description: The service endpoint. Can contain multiple possible values + description: + The service endpoint. Can contain multiple possible values as described in the [Create DID operation] description: A service expressed in the DID document. https://www.w3.org/TR/did-core/#services UpdateManagedDIDRequest: @@ -2402,24 +2487,24 @@ components: actions: type: array items: - $ref: '#/components/schemas/UpdateManagedDIDRequestAction' + $ref: "#/components/schemas/UpdateManagedDIDRequestAction" UpdateManagedDIDRequestAction: required: - actionType type: object properties: actionType: - $ref: '#/components/schemas/ActionType' + $ref: "#/components/schemas/ActionType" addKey: - $ref: '#/components/schemas/ManagedDIDKeyTemplate' + $ref: "#/components/schemas/ManagedDIDKeyTemplate" removeKey: - $ref: '#/components/schemas/RemoveEntryById' + $ref: "#/components/schemas/RemoveEntryById" addService: - $ref: '#/components/schemas/Service' + $ref: "#/components/schemas/Service" removeService: - $ref: '#/components/schemas/RemoveEntryById' + $ref: "#/components/schemas/RemoveEntryById" updateService: - $ref: '#/components/schemas/UpdateManagedDIDServiceAction' + $ref: "#/components/schemas/UpdateManagedDIDServiceAction" description: |- A list of actions to perform on DID document. The field `addKey`, `removeKey`, `addService`, `removeService`, `updateService` must corresponds to @@ -2441,7 +2526,8 @@ components: type: array items: type: string - description: A patch to existing Service. 'type' and 'serviceEndpoint' cannot + description: + A patch to existing Service. 'type' and 'serviceEndpoint' cannot both be empty. VerificationMethod: required: @@ -2464,7 +2550,7 @@ components: description: The DID that controls the verification method. example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff publicKeyJwk: - $ref: '#/components/schemas/PublicKeyJwk' + $ref: "#/components/schemas/PublicKeyJwk" description: A cryptographic public key expressed in the DID document. https://www.w3.org/TR/did-core/#verification-methods VerificationPolicy: required: @@ -2501,7 +2587,7 @@ components: constraints: type: array items: - $ref: '#/components/schemas/VerificationPolicyConstraint' + $ref: "#/components/schemas/VerificationPolicyConstraint" VerificationPolicyConstraint: required: - schemaId @@ -2529,7 +2615,7 @@ components: constraints: type: array items: - $ref: '#/components/schemas/VerificationPolicyConstraint' + $ref: "#/components/schemas/VerificationPolicyConstraint" VerificationPolicyPage: required: - self @@ -2550,4 +2636,4 @@ components: contents: type: array items: - $ref: '#/components/schemas/VerificationPolicy' + $ref: "#/components/schemas/VerificationPolicy" diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala index 95219da3d0..52873d164f 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala @@ -14,7 +14,16 @@ final case class DIDDocumentMetadata( deactivated: Option[Boolean] = None, @description(annotations.canonicalId.description) @encodedExample(annotations.canonicalId.example) - canonicalId: Option[String] = None + canonicalId: Option[String] = None, + @description(annotations.versionId.description) + @encodedExample(annotations.versionId.example) + versionId: Option[String] = None, + @description(annotations.created.description) + @encodedExample(annotations.created.example) + created: Option[String] = None, + @description(annotations.updated.description) + @encodedExample(annotations.updated.example) + updated: Option[String] = None, ) object DIDDocumentMetadata { @@ -36,6 +45,28 @@ object DIDDocumentMetadata { |""".stripMargin, example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" ) + + object versionId + extends Annotation[String]( + description = """ + |DID document metadata MUST contain a versionId property with the hash of the AtalaOperation contained in the latest valid SignedAtalaOperation that created the DID or changed the DID's internal state. + |""".stripMargin, + example = "4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + ) + + object created + extends Annotation[String]( + description = + "The timestamp of the Cardano block that contained the first valid SignedAtalaOperation with a CreateDIDOperation that created the DID.", + example = "2023-02-04T13:52:10Z" + ) + + object updated + extends Annotation[String]( + description = + "The timestamp of the Cardano block that contained the latest valid SignedAtalaOperation that changed the DID's internal state.", + example = "2023-02-04T13:52:10Z" + ) } given encoder: JsonEncoder[DIDDocumentMetadata] = DeriveJsonEncoder.gen[DIDDocumentMetadata] @@ -46,6 +77,9 @@ object DIDDocumentMetadata { (didDocumentMetadata: w3c.DIDDocumentMetadataRepr) => DIDDocumentMetadata( deactivated = Some(didDocumentMetadata.deactivated), - canonicalId = Some(didDocumentMetadata.canonicalId) + canonicalId = didDocumentMetadata.canonicalId, + versionId = Some(didDocumentMetadata.versionId), + created = didDocumentMetadata.created, + updated = didDocumentMetadata.updated ) } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/ManagedDID.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/ManagedDID.scala index 99517c7f03..30cdf230d4 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/ManagedDID.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/ManagedDID.scala @@ -97,7 +97,8 @@ object CreateManagedDidRequest { final case class CreateManagedDidRequestDocumentTemplate( publicKeys: Seq[ManagedDIDKeyTemplate], - services: Seq[Service] + services: Seq[Service], + contexts: Option[Seq[String]] ) object CreateManagedDidRequestDocumentTemplate { @@ -114,7 +115,8 @@ object CreateManagedDidRequestDocumentTemplate { publicKeys = template.publicKeys.map[DIDPublicKeyTemplate](k => k) } yield walletDomain.ManagedDIDTemplate( publicKeys = publicKeys, - services = services + services = services, + contexts = template.contexts.getOrElse(Nil) ) } } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala index 3502731a7a..90d5377a89 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala @@ -19,8 +19,8 @@ object PublicKeyJwk { given Conversion[w3c.PublicKeyJwk, PublicKeyJwk] = (publicKeyJwk: w3c.PublicKeyJwk) => PublicKeyJwk( crv = Some(publicKeyJwk.crv), - x = Some(publicKeyJwk.x), - y = Some(publicKeyJwk.y), + x = publicKeyJwk.x, + y = publicKeyJwk.y, kty = publicKeyJwk.kty ) } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/UpdateManagedDID.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/UpdateManagedDID.scala index cfd4b41429..4f1a8e75d6 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/UpdateManagedDID.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/UpdateManagedDID.scala @@ -25,6 +25,7 @@ enum ActionType { case ADD_SERVICE extends ActionType case REMOVE_SERVICE extends ActionType case UPDATE_SERVICE extends ActionType + case PATCH_CONTEXT extends ActionType } object ActionType { @@ -42,14 +43,15 @@ final case class UpdateManagedDIDRequestAction( removeKey: Option[RemoveEntryById] = None, addService: Option[Service] = None, removeService: Option[RemoveEntryById] = None, - updateService: Option[UpdateManagedDIDServiceAction] = None + updateService: Option[UpdateManagedDIDServiceAction] = None, + patchContext: Option[PatchContextAction] = None ) object UpdateManagedDIDRequestAction { object annotations { val description = """A list of actions to perform on DID document. - |The field `addKey`, `removeKey`, `addService`, `removeService`, `updateService` must corresponds to + |The field `addKey`, `removeKey`, `addService`, `removeService`, `updateService`, `patchContext` must corresponds to |the `actionType` specified. For example, `addKey` must be present when `actionType` is `ADD_KEY`.""".stripMargin } @@ -83,6 +85,10 @@ object UpdateManagedDIDRequestAction { .toRight("updateService property is missing from action type UPDATE_SERVICE") .flatMap(_.toDomain) .map(s => UpdateService(s)) + case ActionType.PATCH_CONTEXT => + action.patchContext + .toRight("patchContext property is missing from action type PATCH_CONTEXT") + .map(i => PatchContext(i.contexts)) } } } @@ -150,3 +156,11 @@ object UpdateManagedDIDServiceAction { ) } } + +final case class PatchContextAction(contexts: Seq[String]) + +object PatchContextAction { + given JsonEncoder[PatchContextAction] = DeriveJsonEncoder.gen[PatchContextAction] + given JsonDecoder[PatchContextAction] = DeriveJsonDecoder.gen[PatchContextAction] + given Schema[PatchContextAction] = Schema.derived +} diff --git a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/crypto/Prism14Apollo.scala b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/crypto/Prism14Apollo.scala index 4a63095fde..734db619e0 100644 --- a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/crypto/Prism14Apollo.scala +++ b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/crypto/Prism14Apollo.scala @@ -90,6 +90,7 @@ final case class Prism14ECPrivateKey(privateKey: io.iohk.atala.prism.crypto.keys } +// TODO: support operation of other key types object Prism14ECKeyFactory extends ECKeyFactory { override def privateKeyFromEncoded(curve: EllipticCurve, bytes: Array[Byte]): Try[ECPrivateKey] = @@ -98,6 +99,7 @@ object Prism14ECKeyFactory extends ECKeyFactory { Try( Prism14ECPrivateKey(EC.INSTANCE.toPrivateKeyFromBytes(bytes)) ) + case crv => Failure(Exception(s"Operation on curve ${crv.name} is not yet supported")) } override def publicKeyFromEncoded(curve: EllipticCurve, bytes: Array[Byte]): Try[ECPublicKey] = @@ -106,12 +108,14 @@ object Prism14ECKeyFactory extends ECKeyFactory { Try(EC.INSTANCE.toPublicKeyFromBytes(bytes)) .orElse(Try(EC.INSTANCE.toPublicKeyFromCompressed(bytes))) .map(Prism14ECPublicKey.apply) + case crv => Failure(Exception(s"Operation on curve ${crv.name} is not yet supported")) } override def publicKeyFromCoordinate(curve: EllipticCurve, x: BigInt, y: BigInt): Try[ECPublicKey] = curve match { case EllipticCurve.SECP256K1 => Try(Prism14ECPublicKey(EC.INSTANCE.toPublicKeyFromBigIntegerCoordinates(x.toKotlinBigInt, y.toKotlinBigInt))) + case crv => Failure(Exception(s"Operation on curve ${crv.name} is not yet supported")) } override def generateKeyPair(curve: EllipticCurve): Task[ECKeyPair] = { @@ -121,6 +125,7 @@ object Prism14ECKeyFactory extends ECKeyFactory { val keyPair = EC.INSTANCE.generateKeyPair() ECKeyPair(Prism14ECPublicKey(keyPair.getPublicKey), Prism14ECPrivateKey(keyPair.getPrivateKey)) } + case crv => ZIO.fail(Exception(s"Operation on curve ${crv.name} is not yet supported")) } } @@ -142,6 +147,7 @@ object Prism14ECKeyFactory extends ECKeyFactory { Prism14ECPrivateKey(prism14KeyPair.getPrivateKey()) ) } + case crv => ZIO.fail(Exception(s"Operation on curve ${crv.name} is not yet supported")) } } diff --git a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/model/ManagedDIDTemplate.scala b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/model/ManagedDIDTemplate.scala index 07f4a8c175..44d1604523 100644 --- a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/model/ManagedDIDTemplate.scala +++ b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/model/ManagedDIDTemplate.scala @@ -5,7 +5,8 @@ import io.iohk.atala.castor.core.model.did.{Service, VerificationRelationship, S final case class ManagedDIDTemplate( publicKeys: Seq[DIDPublicKeyTemplate], - services: Seq[Service] + services: Seq[Service], + contexts: Seq[String] ) final case class DIDPublicKeyTemplate( @@ -21,6 +22,7 @@ object UpdateManagedDIDAction { final case class AddService(service: Service) extends UpdateManagedDIDAction final case class RemoveService(id: String) extends UpdateManagedDIDAction final case class UpdateService(patch: UpdateServicePatch) extends UpdateManagedDIDAction + final case class PatchContext(context: Seq[String]) extends UpdateManagedDIDAction } final case class UpdateServicePatch( diff --git a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/OperationFactory.scala b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/OperationFactory.scala index b53bdb7e75..a334edc7b7 100644 --- a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/OperationFactory.scala +++ b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/OperationFactory.scala @@ -73,7 +73,7 @@ class OperationFactory(apollo: Apollo) { operation = PrismDIDOperation.Create( publicKeys = keysWithCounter._1.map(_._1) ++ Seq(masterKeyOutcome.publicKey), services = didTemplate.services, - context = Seq() // TODO: expose context in the API + context = didTemplate.contexts ) hdKeys = CreateDIDHdKey( keyPaths = keysWithCounter._1.map { case (publicKey, path) => publicKey.id -> path }.toMap, @@ -183,6 +183,7 @@ class OperationFactory(apollo: Apollo) { case UpdateManagedDIDAction.RemoveService(id) => ZIO.succeed(UpdateDIDAction.RemoveService(id)) case UpdateManagedDIDAction.UpdateService(patch) => ZIO.succeed(UpdateDIDAction.UpdateService(patch.id, patch.serviceType, patch.serviceEndpoints)) + case UpdateManagedDIDAction.PatchContext(context) => ZIO.succeed(UpdateDIDAction.PatchContext(context)) } } diff --git a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/UpdateManagedDIDActionValidator.scala b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/UpdateManagedDIDActionValidator.scala index 352b77cc32..4428238293 100644 --- a/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/UpdateManagedDIDActionValidator.scala +++ b/prism-agent/service/wallet-api/src/main/scala/io/iohk/atala/agent/walletapi/util/UpdateManagedDIDActionValidator.scala @@ -14,6 +14,7 @@ object UpdateManagedDIDActionValidator { case UpdateManagedDIDAction.AddService(_) => None case UpdateManagedDIDAction.RemoveService(_) => None case UpdateManagedDIDAction.UpdateService(_) => None + case UpdateManagedDIDAction.PatchContext(_) => None } val reservedKeyIds = keyIds.filter(id => ManagedDIDService.reservedKeyIds.contains(id)) if (reservedKeyIds.nonEmpty) diff --git a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/ManagedDIDServiceSpec.scala b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/ManagedDIDServiceSpec.scala index 7fcd11b912..066958dac6 100644 --- a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/ManagedDIDServiceSpec.scala +++ b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/service/ManagedDIDServiceSpec.scala @@ -95,8 +95,9 @@ object ManagedDIDServiceSpec private def generateDIDTemplate( publicKeys: Seq[DIDPublicKeyTemplate] = Nil, - services: Seq[Service] = Nil - ): ManagedDIDTemplate = ManagedDIDTemplate(publicKeys, services) + services: Seq[Service] = Nil, + context: Seq[String] = Nil + ): ManagedDIDTemplate = ManagedDIDTemplate(publicKeys, services, context) private def resolutionResult( deactivated: Boolean = false, @@ -104,7 +105,10 @@ object ManagedDIDServiceSpec ): (DIDMetadata, DIDData) = { val metadata = DIDMetadata( lastOperationHash = lastOperationHash, - deactivated = deactivated + canonicalId = None, + deactivated = deactivated, + created = None, + updated = None ) val didData = DIDData( id = PrismDID.buildCanonicalFromSuffix("0" * 64).toOption.get, diff --git a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/storage/StorageSpecHelper.scala b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/storage/StorageSpecHelper.scala index 898be357c2..c4afc1dec9 100644 --- a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/storage/StorageSpecHelper.scala +++ b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/storage/StorageSpecHelper.scala @@ -38,7 +38,8 @@ trait StorageSpecHelper extends ApolloSpecHelper { OperationFactory(apollo).makeCreateOperationRandKey("master0")( ManagedDIDTemplate( publicKeys = keyIds.map(DIDPublicKeyTemplate(_, VerificationRelationship.Authentication)), - services = Nil + services = Nil, + contexts = Nil ) ) @@ -47,7 +48,8 @@ trait StorageSpecHelper extends ApolloSpecHelper { didIndex, ManagedDIDTemplate( publicKeys = keyIds.map(DIDPublicKeyTemplate(_, VerificationRelationship.Authentication)), - services = Nil + services = Nil, + contexts = Nil ) ) diff --git a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/ManagedDIDTemplateValidatorSpec.scala b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/ManagedDIDTemplateValidatorSpec.scala index ee4c114565..cae3258cd1 100644 --- a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/ManagedDIDTemplateValidatorSpec.scala +++ b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/ManagedDIDTemplateValidatorSpec.scala @@ -15,7 +15,7 @@ object ManagedDIDTemplateValidatorSpec extends ZIOSpecDefault { override def spec = suite("ManagedDIDTemplateValidator")( test("accept empty DID template") { - val template = ManagedDIDTemplate(publicKeys = Nil, services = Nil) + val template = ManagedDIDTemplate(publicKeys = Nil, services = Nil, contexts = Nil) assert(ManagedDIDTemplateValidator.validate(template))(isRight) }, test("accept valid non-empty DID template") { @@ -32,7 +32,8 @@ object ManagedDIDTemplateValidatorSpec extends ZIOSpecDefault { `type` = ServiceType.Single(ServiceType.Name.fromStringUnsafe("LinkedDomains")), serviceEndpoint = ServiceEndpoint.Single(UriValue.fromString("http://example.com/").toOption.get) ) - ) + ), + contexts = Nil ) assert(ManagedDIDTemplateValidator.validate(template))(isRight) }, @@ -44,7 +45,8 @@ object ManagedDIDTemplateValidatorSpec extends ZIOSpecDefault { purpose = VerificationRelationship.Authentication ) ), - services = Nil + services = Nil, + contexts = Nil ) assert(ManagedDIDTemplateValidator.validate(template))(isLeft) } diff --git a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/OperationFactorySpec.scala b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/OperationFactorySpec.scala index baadb65dbe..937cccbeb1 100644 --- a/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/OperationFactorySpec.scala +++ b/prism-agent/service/wallet-api/src/test/scala/io/iohk/atala/agent/walletapi/util/OperationFactorySpec.scala @@ -36,7 +36,7 @@ object OperationFactorySpec extends ZIOSpecDefault, ApolloSpecHelper { private val makeCreateOpeartionHdKeySpec = suite("makeCreateOpeartionHdKeySpec ")( test("make CrateOperation from same seed is deterministic") { - val didTemplate = ManagedDIDTemplate(Nil, Nil) + val didTemplate = ManagedDIDTemplate(Nil, Nil, Nil) for { result1 <- operationFactory.makeCreateOperationHdKey("master0", seed)(0, didTemplate) (op1, hdKey1) = result1 @@ -46,7 +46,7 @@ object OperationFactorySpec extends ZIOSpecDefault, ApolloSpecHelper { assert(hdKey1)(equalTo(hdKey2)) }, test("make CreateOperation must contain 1 master key") { - val didTemplate = ManagedDIDTemplate(Nil, Nil) + val didTemplate = ManagedDIDTemplate(Nil, Nil, Nil) for { result <- operationFactory.makeCreateOperationHdKey("master-0", seed)(0, didTemplate) (op, hdKey) = result @@ -62,6 +62,7 @@ object OperationFactorySpec extends ZIOSpecDefault, ApolloSpecHelper { DIDPublicKeyTemplate("auth-1", VerificationRelationship.Authentication), DIDPublicKeyTemplate("issue-0", VerificationRelationship.AssertionMethod), ), + Nil, Nil ) for { diff --git a/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt b/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt index 0c5a8c7a7e..cedf10631c 100644 --- a/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt +++ b/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt @@ -45,7 +45,10 @@ data class PublicKeyJwk( @Serializable data class DidDocumentMetadata( var canonicalId: String? = null, + var versionId: String? = null, var deactivated: Boolean? = null, + var created: String? = null, + var updated: String? = null, ): JsonEncoded @Serializable