diff --git a/src/main/kotlin/eu/europa/ec/eudi/openid4vci/Issuance.kt b/src/main/kotlin/eu/europa/ec/eudi/openid4vci/Issuance.kt index 82b50d4b..64248bd5 100644 --- a/src/main/kotlin/eu/europa/ec/eudi/openid4vci/Issuance.kt +++ b/src/main/kotlin/eu/europa/ec/eudi/openid4vci/Issuance.kt @@ -160,6 +160,10 @@ fun interface RequestIssuance { /** * Places a request to the credential issuance endpoint. * + * If the [AuthorizedRequest] contains authorization details for the requested + * [IssuanceRequestPayload.credentialConfigurationIdentifier], then the [requestPayload] must be + * [IssuanceRequestPayload.IdentifierBased] and the credential identifier must be one of the authorized identifiers. + * * @param requestPayload the payload of the request * @param proofsSpecification the specification of proofs to be included in the request * @return the possibly updated [AuthorizedRequest] (if updated it will contain a fresh updated Resource-Server DPoP Nonce) diff --git a/src/main/kotlin/eu/europa/ec/eudi/openid4vci/internal/RequestIssuanceImpl.kt b/src/main/kotlin/eu/europa/ec/eudi/openid4vci/internal/RequestIssuanceImpl.kt index 00ef30dd..af71aa9a 100644 --- a/src/main/kotlin/eu/europa/ec/eudi/openid4vci/internal/RequestIssuanceImpl.kt +++ b/src/main/kotlin/eu/europa/ec/eudi/openid4vci/internal/RequestIssuanceImpl.kt @@ -57,6 +57,8 @@ internal class RequestIssuanceImpl( requestPayload: IssuanceRequestPayload, proofsSpecification: ProofsSpecification, ): Result> = runCatchingCancellable { + validateRequestPayload(requestPayload, credentialIdentifiers.orEmpty()) + val (proofs, proofsDpopNonce) = buildProofs(proofsSpecification, requestPayload.credentialConfigurationIdentifier, grant) val credentialRequest = buildRequest(requestPayload, proofs, credentialIdentifiers.orEmpty()) @@ -75,6 +77,28 @@ internal class RequestIssuanceImpl( updatedAuthorizedRequest to outcome.toPub() } + private fun validateRequestPayload( + requestPayload: IssuanceRequestPayload, + authorizationDetails: Map>, + ) { + if (authorizationDetails.isNotEmpty()) { + val authorizedIdentifiers = authorizationDetails[requestPayload.credentialConfigurationIdentifier] + check(!authorizedIdentifiers.isNullOrEmpty()) { + "No credential identifiers authorized for ${requestPayload.credentialConfigurationIdentifier}" + } + require(requestPayload is IssuanceRequestPayload.IdentifierBased) { + "Authorization detail type of openid_credential require usage of credential identifiers in credential request" + } + require(requestPayload.credentialIdentifier in authorizedIdentifiers) { + "Credential identifier ${requestPayload.credentialIdentifier.value} is not in authorized identifiers $authorizedIdentifiers" + } + } else { + require(requestPayload is IssuanceRequestPayload.ConfigurationBased) { + "Issuance request payload must be of type ConfigurationBased when no credential identifiers are authorized" + } + } + } + private suspend fun buildProofs( proofsSpecification: ProofsSpecification, credentialConfigId: CredentialConfigurationIdentifier, diff --git a/src/test/kotlin/eu/europa/ec/eudi/openid4vci/IssuanceSingleRequestTest.kt b/src/test/kotlin/eu/europa/ec/eudi/openid4vci/IssuanceSingleRequestTest.kt index 2993f8c7..4c8cecbf 100644 --- a/src/test/kotlin/eu/europa/ec/eudi/openid4vci/IssuanceSingleRequestTest.kt +++ b/src/test/kotlin/eu/europa/ec/eudi/openid4vci/IssuanceSingleRequestTest.kt @@ -346,7 +346,7 @@ class IssuanceSingleRequestTest { CredentialConfigurationIdentifier("eu.europa.ec.eudiw.pid_vc_sd_jwt"), CredentialIdentifier("DUMMY"), ) - assertThrows { + assertThrows { with(issuer) { authorizedRequest.request(requestPayload, noKeyAttestationJwtProofsSpec(Curve.P_256, 1)).getOrThrow() } @@ -383,7 +383,7 @@ class IssuanceSingleRequestTest { CredentialConfigurationIdentifier("eu.europa.ec.eudiw.pid_vc_sd_jwt"), CredentialIdentifier("id"), ) - assertThrows { + assertThrows { with(issuer) { authorizedRequest.request(requestPayload, noKeyAttestationJwtProofsSpec(Curve.P_256)).getOrThrow() }