diff --git a/cloud-agent/service/server/src/main/resources/application.conf b/cloud-agent/service/server/src/main/resources/application.conf index 59ea6db03d..6b15792004 100644 --- a/cloud-agent/service/server/src/main/resources/application.conf +++ b/cloud-agent/service/server/src/main/resources/application.conf @@ -44,6 +44,8 @@ pollux { presentationInvitationExpiry = ${?PRESENTATION_INVITATION_EXPIRY} issuanceInvitationExpiry = 300 seconds issuanceInvitationExpiry = ${?ISSUANCE_INVITATION_EXPIRY} + defaultJwtVCOfferDomain = "default-domain" + defaultJwtVCOfferDomain = ${?DEFAULT_JWT_VC_OFFER_DOMAIN} } connect { diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala index 364ff510bc..89b01e3b66 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/agent/server/config/AppConfig.scala @@ -74,6 +74,7 @@ final case class PolluxConfig( didStateSyncTriggerRecurrenceDelay: Duration, presentationInvitationExpiry: Duration, issuanceInvitationExpiry: Duration, + defaultJwtVCOfferDomain: String ) final case class ConnectConfig( database: DatabaseConfig, diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala index 247177c1f9..c594f9d791 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/IssueControllerImpl.scala @@ -83,7 +83,8 @@ class IssueControllerImpl( goalCode = offerContext.goalCode, goal = offerContext.goal, expirationDuration = offerContext.expirationDuration, - connectionId = request.connectionId + connectionId = request.connectionId, + domain = request.domain.getOrElse(appConfig.pollux.defaultJwtVCOfferDomain) ) } yield record case SDJWT => @@ -108,7 +109,8 @@ class IssueControllerImpl( goalCode = offerContext.goalCode, goal = offerContext.goal, expirationDuration = offerContext.expirationDuration, - connectionId = request.connectionId + connectionId = request.connectionId, + domain = request.domain.getOrElse(appConfig.pollux.defaultJwtVCOfferDomain) ) } yield record case AnonCreds => diff --git a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/CreateIssueCredentialRecordRequest.scala b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/CreateIssueCredentialRecordRequest.scala index ba29538d13..2effd2cd19 100644 --- a/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/CreateIssueCredentialRecordRequest.scala +++ b/cloud-agent/service/server/src/main/scala/org/hyperledger/identus/issue/controller/http/CreateIssueCredentialRecordRequest.scala @@ -69,6 +69,9 @@ final case class CreateIssueCredentialRecordRequest( @description(annotations.goal.description) @encodedExample(annotations.goal.example) goal: Option[String] = None, + @description(annotations.domain.description) + @encodedExample(annotations.domain.example) + domain: Option[String] = None, @description(annotations.jwtVcPropertiesV1.description) jwtVcPropertiesV1: Option[JwtVCPropertiesV1] = None, @description(annotations.anoncredsVcPropertiesV1.description) @@ -372,6 +375,16 @@ object CreateIssueCredentialRecordRequest { example = Some("To issue a Faber College Graduate credential") ) + object domain + extends Annotation[Option[String]]( + description = + """ + | A string that specifies the intended scope or audience for the offer request. The 'domain' field binds the proof or presentation to a particular context (e.g., application, service, or verifier) to prevent misuse. + | It is often used alongside a 'challenge' field to ensure the freshness and uniqueness of the proof. The 'domain' field adds context to validate the origin or purpose of the proof. + |""".stripMargin, + example = Some("faber-college-jwt-vc") + ) + object jwtVcPropertiesV1 extends Annotation[Option[JwtVCPropertiesV1]]( description = """ diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala index 1418a3e87a..ff465e506f 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialService.scala @@ -37,6 +37,7 @@ trait CredentialService { goal: Option[String], expirationDuration: Option[Duration], connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] def createSDJWTIssueCredentialRecord( @@ -53,6 +54,7 @@ trait CredentialService { goal: Option[String], expirationDuration: Option[Duration], connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] def createAnonCredsIssueCredentialRecord( @@ -67,7 +69,7 @@ trait CredentialService { goalCode: Option[String], goal: Option[String], expirationDuration: Option[Duration], - connectionId: Option[UUID], + connectionId: Option[UUID] ): URIO[WalletAccessContext, IssueCredentialRecord] /** Return a list of records as well as a count of all filtered items */ diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala index c21c1eb2fe..56819c1e26 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceImpl.scala @@ -210,6 +210,7 @@ class CredentialServiceImpl( goal: Option[String], expirationDuration: Option[Duration], connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] = { for { _ <- validateClaimsAgainstSchemaIfAny(claims, credentialSchemaRef.map(List(_))) @@ -221,7 +222,7 @@ class CredentialServiceImpl( claims = attributes, thid = thid, UUID.randomUUID().toString, - "domain", // TODO remove the hardcoded domain + domain, IssueCredentialOfferFormat.JWT ) record <- createIssueCredentialRecord( @@ -258,6 +259,7 @@ class CredentialServiceImpl( goal: Option[String], expirationDuration: Option[Duration], connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] = { val maybeSchemaIds = credentialSchemaRef.map(ref => List(ref.id)) for { @@ -270,7 +272,7 @@ class CredentialServiceImpl( claims = attributes, thid = thid, UUID.randomUUID().toString, - "domain", + domain, IssueCredentialOfferFormat.SDJWT ) record <- createIssueCredentialRecord( diff --git a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala index f086bac1b9..d1ab7b7b5c 100644 --- a/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala +++ b/pollux/core/src/main/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceNotifier.scala @@ -38,6 +38,7 @@ class CredentialServiceNotifier( goal: Option[String], expirationDuration: Option[Duration], connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] = notifyOnSuccess( svc.createJWTIssueCredentialRecord( @@ -53,7 +54,8 @@ class CredentialServiceNotifier( goalCode, goal, expirationDuration, - connectionId + connectionId, + domain ) ) @@ -71,6 +73,7 @@ class CredentialServiceNotifier( goal: Option[String], expirationDuration: Option[Duration], connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] = notifyOnSuccess( svc.createSDJWTIssueCredentialRecord( @@ -86,7 +89,8 @@ class CredentialServiceNotifier( goalCode, goal, expirationDuration, - connectionId + connectionId, + domain ) ) diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala index 331fe01ac5..9eedbde832 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/CredentialServiceSpecHelper.scala @@ -142,7 +142,8 @@ trait CredentialServiceSpecHelper { goalCode = None, goal = None, expirationDuration = None, - connectionId = Some(UUID.randomUUID()) + connectionId = Some(UUID.randomUUID()), + domain = "domain" ) } yield record diff --git a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala index 32fbf4b8dc..0a922bf0fc 100644 --- a/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala +++ b/pollux/core/src/test/scala/org/hyperledger/identus/pollux/core/service/MockCredentialService.scala @@ -139,7 +139,8 @@ object MockCredentialService extends Mock[CredentialService] { goalCode: Option[String], goal: Option[String], expirationDuration: Option[Duration], - connectionId: Option[UUID] + connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] = proxy( CreateJWTIssueCredentialRecord, @@ -170,7 +171,8 @@ object MockCredentialService extends Mock[CredentialService] { goalCode: Option[String], goal: Option[String], expirationDuration: Option[Duration], - connectionId: Option[UUID] + connectionId: Option[UUID], + domain: String ): URIO[WalletAccessContext, IssueCredentialRecord] = proxy( CreateSDJWTIssueCredentialRecord,