From 2b31b2155a225cfe317e8ba54008aedb756718aa Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 17:22:50 +0200 Subject: [PATCH 1/8] feat: check strict validation --- cmd/vc-rest/startcmd/start.go | 1 + pkg/service/oidc4ci/api.go | 9 +- pkg/service/oidc4ci/oidc4ci_service.go | 11 +- .../oidc4ci_service_initiate_issuance.go | 48 +++- .../oidc4ci_service_initiate_issuance_test.go | 110 +++++++- .../oidc4ci/testdata/credentials.jsonld | 237 ++++++++++++++++++ 6 files changed, 402 insertions(+), 14 deletions(-) create mode 100644 pkg/service/oidc4ci/testdata/credentials.jsonld diff --git a/cmd/vc-rest/startcmd/start.go b/cmd/vc-rest/startcmd/start.go index 1e2481381..10882d422 100644 --- a/cmd/vc-rest/startcmd/start.go +++ b/cmd/vc-rest/startcmd/start.go @@ -721,6 +721,7 @@ func buildEchoHandler( TrustRegistry: trustRegistryService, AckService: ackService, Composer: oidc4ci.NewCredentialComposer(), + DocumentLoader: documentLoader, }) if err != nil { return nil, fmt.Errorf("failed to instantiate new oidc4ci service: %w", err) diff --git a/pkg/service/oidc4ci/api.go b/pkg/service/oidc4ci/api.go index 86941dfbd..7e6f333dc 100644 --- a/pkg/service/oidc4ci/api.go +++ b/pkg/service/oidc4ci/api.go @@ -195,10 +195,11 @@ type InitiateIssuanceCredentialConfiguration struct { } type InitiateIssuanceComposeCredential struct { - Credential *map[string]interface{} `json:"credential,omitempty"` - IDTemplate string `json:"id_template"` - OverrideIssuer bool `json:"override_issuer"` - OverrideSubjectDID bool `json:"override_subject_did"` + Credential *map[string]interface{} `json:"credential,omitempty"` + IDTemplate string `json:"id_template"` + OverrideIssuer bool `json:"override_issuer"` + OverrideSubjectDID bool `json:"override_subject_did"` + EnableStrictValidation bool `json:"enable_strict_validation,omitempty"` } // InitiateIssuanceResponse is the response from the Issuer to the Wallet with initiate issuance URL. diff --git a/pkg/service/oidc4ci/oidc4ci_service.go b/pkg/service/oidc4ci/oidc4ci_service.go index 06fa375f1..b744812f1 100644 --- a/pkg/service/oidc4ci/oidc4ci_service.go +++ b/pkg/service/oidc4ci/oidc4ci_service.go @@ -4,7 +4,7 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -//go:generate mockgen -destination oidc4ci_service_mocks_test.go -self_package mocks -package oidc4ci_test -source=oidc4ci_service.go -mock_names transactionStore=MockTransactionStore,wellKnownService=MockWellKnownService,eventService=MockEventService,pinGenerator=MockPinGenerator,credentialOfferReferenceStore=MockCredentialOfferReferenceStore,claimDataStore=MockClaimDataStore,profileService=MockProfileService,dataProtector=MockDataProtector,kmsRegistry=MockKMSRegistry,cryptoJWTSigner=MockCryptoJWTSigner,jsonSchemaValidator=MockJSONSchemaValidator,trustRegistry=MockTrustRegistry,ackStore=MockAckStore,ackService=MockAckService,composer=MockComposer +//go:generate mockgen -destination oidc4ci_service_mocks_test.go -self_package mocks -package oidc4ci_test -source=oidc4ci_service.go -mock_names transactionStore=MockTransactionStore,wellKnownService=MockWellKnownService,eventService=MockEventService,pinGenerator=MockPinGenerator,credentialOfferReferenceStore=MockCredentialOfferReferenceStore,claimDataStore=MockClaimDataStore,profileService=MockProfileService,dataProtector=MockDataProtector,kmsRegistry=MockKMSRegistry,cryptoJWTSigner=MockCryptoJWTSigner,jsonSchemaValidator=MockJSONSchemaValidator,trustRegistry=MockTrustRegistry,ackStore=MockAckStore,ackService=MockAckService,composer=MockComposer,documentLoader=MockDocumentLoader package oidc4ci @@ -19,6 +19,7 @@ import ( "time" "github.com/google/uuid" + "github.com/piprate/json-gold/ld" "github.com/samber/lo" util "github.com/trustbloc/did-go/doc/util/time" "github.com/trustbloc/logutil-go/pkg/log" @@ -153,6 +154,11 @@ type composer interface { ) (*verifiable.Credential, error) } +// DocumentLoader knows how to load remote documents. +type documentLoader interface { + LoadDocument(u string) (*ld.RemoteDocument, error) +} + // Config holds configuration options and dependencies for Service. type Config struct { TransactionStore transactionStore @@ -173,6 +179,7 @@ type Config struct { TrustRegistry trustRegistry AckService ackService Composer composer + DocumentLoader documentLoader } // Service implements VCS credential interaction API for OIDC credential issuance. @@ -195,6 +202,7 @@ type Service struct { trustRegistry trustRegistry ackService ackService composer composer + documentLoader documentLoader } // NewService returns a new Service instance. @@ -218,6 +226,7 @@ func NewService(config *Config) (*Service, error) { trustRegistry: config.TrustRegistry, ackService: config.AckService, composer: config.Composer, + documentLoader: config.DocumentLoader, }, nil } diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index bbc5e0c7e..2204fedf2 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -19,6 +19,7 @@ import ( "github.com/samber/lo" "github.com/trustbloc/logutil-go/pkg/log" "github.com/trustbloc/vc-go/jwt" + verifiable2 "github.com/trustbloc/vc-go/verifiable" "github.com/trustbloc/vcs/internal/logfields" "github.com/trustbloc/vcs/pkg/doc/vc" @@ -183,10 +184,17 @@ func (s *Service) newTxCredentialConf( var targetCredentialTemplate *profileapi.CredentialTemplate - if credentialConfiguration.CredentialTemplateID == "" && - credentialConfiguration.ComposeCredential != nil && - credentialConfiguration.ComposeCredential.Credential != nil { + isCompose := credentialConfiguration.ComposeCredential != nil && + credentialConfiguration.ComposeCredential.Credential != nil + + if credentialConfiguration.CredentialTemplateID == "" && isCompose { targetCredentialTemplate = s.buildVirtualTemplate(&credentialConfiguration) + + if targetCredentialTemplate.Checks.Strict { + if err = s.validateComposeCredential(*credentialConfiguration.ComposeCredential.Credential); err != nil { + return nil, err + } + } } else { targetCredentialTemplate, err = findCredentialTemplate(credentialConfiguration.CredentialTemplateID, profile) if err != nil { @@ -235,9 +243,43 @@ func (s *Service) newTxCredentialConf( return txCredentialConfiguration, nil } +func (s *Service) validateComposeCredential(credential map[string]interface{}) error { + requiredFields := map[string]string{ + "issuer": "did:orb:anything", + "issuanceDate": "2021-01-01T00:00:00Z", + } + + var missingFieldsAdded []string + + for key, value := range requiredFields { + if _, ok := credential[key]; !ok { + credential[key] = value + missingFieldsAdded = append(missingFieldsAdded, key) + } + } + + if _, credCheckErr := verifiable2.ParseCredentialJSON(credential, + verifiable2.WithJSONLDDocumentLoader(s.documentLoader), + verifiable2.WithDisabledProofCheck(), + verifiable2.WithStrictValidation(), + ); credCheckErr != nil { + return resterr.NewValidationError(resterr.InvalidValue, "credential", + fmt.Errorf("parse credential: %w", credCheckErr)) + } + + for _, key := range missingFieldsAdded { + delete(credential, key) + } + + return nil +} + func (s *Service) buildVirtualTemplate(req *InitiateIssuanceCredentialConfiguration) *profileapi.CredentialTemplate { result := &profileapi.CredentialTemplate{ ID: fmt.Sprintf("virtual_%s", uuid.NewString()), + Checks: profileapi.CredentialTemplateChecks{ + Strict: req.ComposeCredential.EnableStrictValidation, + }, } if req.ComposeCredential.Credential != nil { diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index d96e99878..58b344b08 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -12,10 +12,12 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "testing" "time" "github.com/golang/mock/gomock" + jsonld "github.com/piprate/json-gold/ld" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -52,6 +54,7 @@ type mocks struct { crypto *MockDataProtector jsonSchemaValidator *MockJSONSchemaValidator ackService *MockAckService + documentLoader *jsonld.DefaultDocumentLoader } func TestService_InitiateIssuance(t *testing.T) { @@ -723,7 +726,14 @@ func TestService_InitiateIssuance(t *testing.T) { setup: func(mocks *mocks) { initialOpState := "eyJhbGciOiJSU0Et" expectedCode := "super-secret-pre-auth-code" - claimData := degreeClaims + claimData := map[string]interface{}{ + "name": "John Doe", + "spouse": "Jane Doe", + "degree": map[string]interface{}{ + "type": "BachelorDegree", + "degree": "MIT", + }, + } profile = &testProfile mocks.transactionStore.EXPECT().Create(gomock.Any(), int32(0), gomock.Any()). @@ -790,6 +800,18 @@ func TestService_InitiateIssuance(t *testing.T) { mocks.wellKnownService.EXPECT().GetOIDCConfiguration(gomock.Any(), walletWellKnownURL).Return( &oidc4ci.IssuerIDPOIDCConfiguration{}, nil) + targetCred := map[string]interface{}{ + "type": []string{ + "VerifiableCredential", + "PermanentResidentCard", + }, + "@context": []string{ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + }, + "credentialSubject": claimData, + } + issuanceReq = &oidc4ci.InitiateIssuanceRequest{ ClientInitiateIssuanceURL: "", ClientWellKnownURL: walletWellKnownURL, @@ -803,9 +825,10 @@ func TestService_InitiateIssuance(t *testing.T) { { CredentialTemplateID: "templateID", ComposeCredential: &oidc4ci.InitiateIssuanceComposeCredential{ - Credential: &claimData, - IDTemplate: "some-template", - OverrideIssuer: true, + Credential: &targetCred, + IDTemplate: "some-template", + OverrideIssuer: true, + EnableStrictValidation: true, }, }, }, @@ -823,7 +846,14 @@ func TestService_InitiateIssuance(t *testing.T) { setup: func(mocks *mocks) { initialOpState := "eyJhbGciOiJSU0Et" expectedCode := "super-secret-pre-auth-code" - claimData := degreeClaims + claimData := map[string]interface{}{ + "name": "John Doe", + "spouse": "Jane Doe", + "degree": map[string]interface{}{ + "type": "BachelorDegree", + "degree": "MIT", + }, + } var tempProfile *profileapi.Issuer require.NoError(t, json.Unmarshal(profileWithoutTemplateJSON, &tempProfile)) // hack profile ref @@ -898,9 +928,12 @@ func TestService_InitiateIssuance(t *testing.T) { "VerifiableCredential", "PermanentResidentCard", }, - "context": []string{ + "@context": []string{ + "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1", }, + "issuer": "did:orb:anything", + "issuanceDate": "2020-03-10T04:24:12.164Z", "credentialSubject": claimData, } @@ -935,6 +968,67 @@ func TestService_InitiateIssuance(t *testing.T) { resp.InitiateIssuanceURL) }, }, + { + name: "Fail Compose feature with strict validation", + setup: func(mocks *mocks) { + initialOpState := "eyJhbGciOiJSU0Et" + claimData := map[string]interface{}{ + "name": "John Doe", + "spouse": "Jane Doe", + "totally-random-field-not-in-jsonld": "should not be here", + "degree": map[string]interface{}{ + "type": "BachelorDegree", + "degree": "MIT", + }, + } + + var tempProfile *profileapi.Issuer + require.NoError(t, json.Unmarshal(profileWithoutTemplateJSON, &tempProfile)) // hack profile ref + profile = tempProfile + + targetCred := map[string]interface{}{ + "type": []string{ + "VerifiableCredential", + "PermanentResidentCard", + }, + "@context": []string{ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + }, + "issuer": "did:orb:anything", + "issuanceDate": "2020-03-10T04:24:12.164Z", + "credentialSubject": claimData, + } + + targetCredBytes, err := json.Marshal(targetCred) + require.NoError(t, err) + assert.NoError(t, json.Unmarshal(targetCredBytes, &targetCred)) // just to ensure type castings + + issuanceReq = &oidc4ci.InitiateIssuanceRequest{ + ClientInitiateIssuanceURL: "", + ClientWellKnownURL: walletWellKnownURL, + GrantType: oidc4ci.GrantTypePreAuthorizedCode, + ResponseType: "", + Scope: []string{"openid", "profile"}, + OpState: initialOpState, + UserPinRequired: false, + WalletInitiatedIssuance: false, + CredentialConfiguration: []oidc4ci.InitiateIssuanceCredentialConfiguration{ + { + ComposeCredential: &oidc4ci.InitiateIssuanceComposeCredential{ + Credential: &targetCred, + IDTemplate: "some-template", + OverrideIssuer: true, + EnableStrictValidation: true, + }, + }, + }, + } + }, + check: func(t *testing.T, resp *oidc4ci.InitiateIssuanceResponse, err error) { + require.Error(t, err, "JSON-LD doc has different structure after compaction") + }, + }, { name: "Success Pre-Auth without PIN and without template and empty state", setup: func(mocks *mocks) { @@ -1772,6 +1866,7 @@ func TestService_InitiateIssuance(t *testing.T) { pinGenerator: NewMockPinGenerator(gomock.NewController(t)), crypto: NewMockDataProtector(gomock.NewController(t)), jsonSchemaValidator: NewMockJSONSchemaValidator(gomock.NewController(t)), + documentLoader: jsonld.NewDefaultDocumentLoader(http.DefaultClient), } tt.setup(m) @@ -1787,6 +1882,7 @@ func TestService_InitiateIssuance(t *testing.T) { DataProtector: m.crypto, JSONSchemaValidator: m.jsonSchemaValidator, Composer: m.composer, + DocumentLoader: m.documentLoader, }) require.NoError(t, err) @@ -1845,6 +1941,7 @@ func TestService_InitiateIssuanceWithRemoteStore(t *testing.T) { referenceStore = NewMockCredentialOfferReferenceStore(gomock.NewController(t)) kmsRegistry = NewMockKMSRegistry(gomock.NewController(t)) cryptoJWTSigner = NewMockCryptoJWTSigner(gomock.NewController(t)) + documentLoader = NewMockDocumentLoader(gomock.NewController(t)) issuanceReq *oidc4ci.InitiateIssuanceRequest profile *profileapi.Issuer ) @@ -2245,6 +2342,7 @@ func TestService_InitiateIssuanceWithRemoteStore(t *testing.T) { KMSRegistry: kmsRegistry, CryptoJWTSigner: cryptoJWTSigner, EventTopic: spi.IssuerEventTopic, + DocumentLoader: documentLoader, }) require.NoError(t, err) diff --git a/pkg/service/oidc4ci/testdata/credentials.jsonld b/pkg/service/oidc4ci/testdata/credentials.jsonld new file mode 100644 index 000000000..26169278c --- /dev/null +++ b/pkg/service/oidc4ci/testdata/credentials.jsonld @@ -0,0 +1,237 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "VerifiableCredential": { + "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "credentialSchema": { + "@id": "cred:credentialSchema", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + + "JsonSchemaValidator2018": "cred:JsonSchemaValidator2018" + } + }, + "credentialStatus": {"@id": "cred:credentialStatus", "@type": "@id"}, + "credentialSubject": {"@id": "cred:credentialSubject", "@type": "@id"}, + "evidence": {"@id": "cred:evidence", "@type": "@id"}, + "expirationDate": {"@id": "cred:expirationDate", "@type": "xsd:dateTime"}, + "holder": {"@id": "cred:holder", "@type": "@id"}, + "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"}, + "issuer": {"@id": "cred:issuer", "@type": "@id"}, + "issuanceDate": {"@id": "cred:issuanceDate", "@type": "xsd:dateTime"}, + "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, + "refreshService": { + "@id": "cred:refreshService", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + + "ManualRefreshService2018": "cred:ManualRefreshService2018" + } + }, + "termsOfUse": {"@id": "cred:termsOfUse", "@type": "@id"}, + "validFrom": {"@id": "cred:validFrom", "@type": "xsd:dateTime"}, + "validUntil": {"@id": "cred:validUntil", "@type": "xsd:dateTime"} + } + }, + + "VerifiablePresentation": { + "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + + "holder": {"@id": "cred:holder", "@type": "@id"}, + "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, + "verifiableCredential": {"@id": "cred:verifiableCredential", "@type": "@id", "@container": "@graph"} + } + }, + + "EcdsaSecp256k1Signature2019": { + "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "EcdsaSecp256r1Signature2019": { + "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "Ed25519Signature2018": { + "@id": "https://w3id.org/security#Ed25519Signature2018", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "RsaSignature2018": { + "@id": "https://w3id.org/security#RsaSignature2018", + "@context": { + "@version": 1.1, + "@protected": true, + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "proof": {"@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph"} + } +} \ No newline at end of file From 06a0af563ba4b9597c6e55cf219fd0c91c07f793 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 18:34:46 +0200 Subject: [PATCH 2/8] feat: add perform strict validation --- api/spec/openapi.gen.go | 383 +++++++++--------- docs/v1/openapi.yaml | 8 + pkg/restapi/v1/issuer/controller.go | 18 +- pkg/restapi/v1/issuer/openapi.gen.go | 6 + pkg/service/oidc4ci/api.go | 10 +- .../oidc4ci_service_initiate_issuance.go | 2 +- .../oidc4ci_service_initiate_issuance_test.go | 16 +- 7 files changed, 230 insertions(+), 213 deletions(-) diff --git a/api/spec/openapi.gen.go b/api/spec/openapi.gen.go index 164a86605..7ef423a5f 100644 --- a/api/spec/openapi.gen.go +++ b/api/spec/openapi.gen.go @@ -20,198 +20,199 @@ import ( var swaggerSpec = []string{ "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", + "K2Q7nprv9F/1Hz+///z/AwAA//8nT9Phxx8BAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/docs/v1/openapi.yaml b/docs/v1/openapi.yaml index 11e55082d..4cc9f4766 100644 --- a/docs/v1/openapi.yaml +++ b/docs/v1/openapi.yaml @@ -1517,6 +1517,10 @@ components: type: boolean description: Override credential subject did. nullable: true + perform_strict_validation: + type: boolean + description: Perform strict validation. + nullable: true credential: type: object description: Raw Complete credential for sign and customization @@ -2250,6 +2254,10 @@ components: type: boolean description: Override credential subject did. nullable: true + credential_perform_strict_validation: + type: boolean + description: Perform strict validation. + nullable: true credential: type: object description: Raw Complete credential for sign and customization diff --git a/pkg/restapi/v1/issuer/controller.go b/pkg/restapi/v1/issuer/controller.go index e6fa93349..acdc9ff79 100644 --- a/pkg/restapi/v1/issuer/controller.go +++ b/pkg/restapi/v1/issuer/controller.go @@ -425,10 +425,11 @@ func (c *Controller) InitiateCredentialComposeIssuance(e echo.Context, profileID for _, compose := range lo.FromPtr(body.Compose) { configs = append(configs, InitiateIssuanceCredentialConfiguration{ Compose: &DeprecatedComposeOIDC4CICredential{ - Credential: compose.Credential, - IdTemplate: compose.CredentialOverrideId, - OverrideIssuer: compose.CredentialOverrideIssuer, - OverrideSubjectDid: compose.CredentialOverrideSubjectDid, + Credential: compose.Credential, + IdTemplate: compose.CredentialOverrideId, + OverrideIssuer: compose.CredentialOverrideIssuer, + OverrideSubjectDid: compose.CredentialOverrideSubjectDid, + PerformStrictValidation: compose.CredentialPerformStrictValidation, }, CredentialExpiresAt: compose.CredentialExpiresAt, }) @@ -524,10 +525,11 @@ func (c *Controller) initiateIssuance( if multiCredentialIssuance.Compose != nil { credConfig.ComposeCredential = &oidc4ci.InitiateIssuanceComposeCredential{ - Credential: multiCredentialIssuance.Compose.Credential, - IDTemplate: lo.FromPtr(multiCredentialIssuance.Compose.IdTemplate), - OverrideIssuer: lo.FromPtr(multiCredentialIssuance.Compose.OverrideIssuer), - OverrideSubjectDID: lo.FromPtr(multiCredentialIssuance.Compose.OverrideSubjectDid), + Credential: multiCredentialIssuance.Compose.Credential, + IDTemplate: lo.FromPtr(multiCredentialIssuance.Compose.IdTemplate), + OverrideIssuer: lo.FromPtr(multiCredentialIssuance.Compose.OverrideIssuer), + OverrideSubjectDID: lo.FromPtr(multiCredentialIssuance.Compose.OverrideSubjectDid), + PerformStrictValidation: lo.FromPtr(multiCredentialIssuance.Compose.PerformStrictValidation), } } diff --git a/pkg/restapi/v1/issuer/openapi.gen.go b/pkg/restapi/v1/issuer/openapi.gen.go index d050fdf69..4f06a7c60 100644 --- a/pkg/restapi/v1/issuer/openapi.gen.go +++ b/pkg/restapi/v1/issuer/openapi.gen.go @@ -143,6 +143,9 @@ type DeprecatedComposeOIDC4CICredential struct { // Override credential subject did. OverrideSubjectDid *bool `json:"override_subject_did"` + + // Perform strict validation. + PerformStrictValidation *bool `json:"perform_strict_validation"` } // Model for exchanging auth code from issuer oauth @@ -204,6 +207,9 @@ type InitiateIssuanceCredentialConfigurationCompose struct { // Override credential subject did. CredentialOverrideSubjectDid *bool `json:"credential_override_subject_did"` + + // Perform strict validation. + CredentialPerformStrictValidation *bool `json:"credential_perform_strict_validation"` } // Model for Initiate OIDC Compose Credential Issuance Request. diff --git a/pkg/service/oidc4ci/api.go b/pkg/service/oidc4ci/api.go index 7e6f333dc..f33aec502 100644 --- a/pkg/service/oidc4ci/api.go +++ b/pkg/service/oidc4ci/api.go @@ -195,11 +195,11 @@ type InitiateIssuanceCredentialConfiguration struct { } type InitiateIssuanceComposeCredential struct { - Credential *map[string]interface{} `json:"credential,omitempty"` - IDTemplate string `json:"id_template"` - OverrideIssuer bool `json:"override_issuer"` - OverrideSubjectDID bool `json:"override_subject_did"` - EnableStrictValidation bool `json:"enable_strict_validation,omitempty"` + Credential *map[string]interface{} `json:"credential,omitempty"` + IDTemplate string `json:"id_template"` + OverrideIssuer bool `json:"override_issuer"` + OverrideSubjectDID bool `json:"override_subject_did"` + PerformStrictValidation bool `json:"perform_strict_validation,omitempty"` } // InitiateIssuanceResponse is the response from the Issuer to the Wallet with initiate issuance URL. diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index 2204fedf2..09da5e786 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -278,7 +278,7 @@ func (s *Service) buildVirtualTemplate(req *InitiateIssuanceCredentialConfigurat result := &profileapi.CredentialTemplate{ ID: fmt.Sprintf("virtual_%s", uuid.NewString()), Checks: profileapi.CredentialTemplateChecks{ - Strict: req.ComposeCredential.EnableStrictValidation, + Strict: req.ComposeCredential.PerformStrictValidation, }, } diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index 58b344b08..1a1fc6f82 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -825,10 +825,10 @@ func TestService_InitiateIssuance(t *testing.T) { { CredentialTemplateID: "templateID", ComposeCredential: &oidc4ci.InitiateIssuanceComposeCredential{ - Credential: &targetCred, - IDTemplate: "some-template", - OverrideIssuer: true, - EnableStrictValidation: true, + Credential: &targetCred, + IDTemplate: "some-template", + OverrideIssuer: true, + PerformStrictValidation: true, }, }, }, @@ -1016,10 +1016,10 @@ func TestService_InitiateIssuance(t *testing.T) { CredentialConfiguration: []oidc4ci.InitiateIssuanceCredentialConfiguration{ { ComposeCredential: &oidc4ci.InitiateIssuanceComposeCredential{ - Credential: &targetCred, - IDTemplate: "some-template", - OverrideIssuer: true, - EnableStrictValidation: true, + Credential: &targetCred, + IDTemplate: "some-template", + OverrideIssuer: true, + PerformStrictValidation: true, }, }, }, From cc9d69603d2231fad52054deee0c036a868f81f5 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 18:39:30 +0200 Subject: [PATCH 3/8] chore: cleanup --- .../oidc4ci/testdata/credentials.jsonld | 237 ------------------ 1 file changed, 237 deletions(-) delete mode 100644 pkg/service/oidc4ci/testdata/credentials.jsonld diff --git a/pkg/service/oidc4ci/testdata/credentials.jsonld b/pkg/service/oidc4ci/testdata/credentials.jsonld deleted file mode 100644 index 26169278c..000000000 --- a/pkg/service/oidc4ci/testdata/credentials.jsonld +++ /dev/null @@ -1,237 +0,0 @@ -{ - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "VerifiableCredential": { - "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "credentialSchema": { - "@id": "cred:credentialSchema", - "@type": "@id", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - - "JsonSchemaValidator2018": "cred:JsonSchemaValidator2018" - } - }, - "credentialStatus": {"@id": "cred:credentialStatus", "@type": "@id"}, - "credentialSubject": {"@id": "cred:credentialSubject", "@type": "@id"}, - "evidence": {"@id": "cred:evidence", "@type": "@id"}, - "expirationDate": {"@id": "cred:expirationDate", "@type": "xsd:dateTime"}, - "holder": {"@id": "cred:holder", "@type": "@id"}, - "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"}, - "issuer": {"@id": "cred:issuer", "@type": "@id"}, - "issuanceDate": {"@id": "cred:issuanceDate", "@type": "xsd:dateTime"}, - "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, - "refreshService": { - "@id": "cred:refreshService", - "@type": "@id", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - - "ManualRefreshService2018": "cred:ManualRefreshService2018" - } - }, - "termsOfUse": {"@id": "cred:termsOfUse", "@type": "@id"}, - "validFrom": {"@id": "cred:validFrom", "@type": "xsd:dateTime"}, - "validUntil": {"@id": "cred:validUntil", "@type": "xsd:dateTime"} - } - }, - - "VerifiablePresentation": { - "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "cred": "https://www.w3.org/2018/credentials#", - "sec": "https://w3id.org/security#", - - "holder": {"@id": "cred:holder", "@type": "@id"}, - "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, - "verifiableCredential": {"@id": "cred:verifiableCredential", "@type": "@id", "@container": "@graph"} - } - }, - - "EcdsaSecp256k1Signature2019": { - "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "EcdsaSecp256r1Signature2019": { - "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "Ed25519Signature2018": { - "@id": "https://w3id.org/security#Ed25519Signature2018", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - "xsd": "http://www.w3.org/2001/XMLSchema#", - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "RsaSignature2018": { - "@id": "https://w3id.org/security#RsaSignature2018", - "@context": { - "@version": 1.1, - "@protected": true, - - "challenge": "sec:challenge", - "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, - "domain": "sec:domain", - "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, - "jws": "sec:jws", - "nonce": "sec:nonce", - "proofPurpose": { - "@id": "sec:proofPurpose", - "@type": "@vocab", - "@context": { - "@version": 1.1, - "@protected": true, - - "id": "@id", - "type": "@type", - - "sec": "https://w3id.org/security#", - - "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, - "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} - } - }, - "proofValue": "sec:proofValue", - "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} - } - }, - - "proof": {"@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph"} - } -} \ No newline at end of file From 38babff2e3fbfc2ef9b3f824c250d5c5337d8710 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 18:49:51 +0200 Subject: [PATCH 4/8] fix: nest if --- pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index 09da5e786..9c607e231 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -168,6 +168,7 @@ func (s *Service) validateFlowSpecificRequestParams( return nil } +//nolint:nestif func (s *Service) newTxCredentialConf( ctx context.Context, credentialConfiguration InitiateIssuanceCredentialConfiguration, From bfa6d9201f685f916e3472096c054538aab2abcd Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 18:54:10 +0200 Subject: [PATCH 5/8] fix: nest --- pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index 9c607e231..ddf3692fa 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -168,7 +168,6 @@ func (s *Service) validateFlowSpecificRequestParams( return nil } -//nolint:nestif func (s *Service) newTxCredentialConf( ctx context.Context, credentialConfiguration InitiateIssuanceCredentialConfiguration, @@ -188,10 +187,10 @@ func (s *Service) newTxCredentialConf( isCompose := credentialConfiguration.ComposeCredential != nil && credentialConfiguration.ComposeCredential.Credential != nil - if credentialConfiguration.CredentialTemplateID == "" && isCompose { + if credentialConfiguration.CredentialTemplateID == "" && isCompose { //nolint:nestif targetCredentialTemplate = s.buildVirtualTemplate(&credentialConfiguration) - if targetCredentialTemplate.Checks.Strict { + if targetCredentialTemplate.Checks.Strict { //nolint:nestif if err = s.validateComposeCredential(*credentialConfiguration.ComposeCredential.Credential); err != nil { return nil, err } From bfc75d62bd3023f06f42b7e78583b6becda6f2fa Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 19:07:20 +0200 Subject: [PATCH 6/8] chore: better coverage --- pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index 1a1fc6f82..b30f38270 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -932,8 +932,6 @@ func TestService_InitiateIssuance(t *testing.T) { "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1", }, - "issuer": "did:orb:anything", - "issuanceDate": "2020-03-10T04:24:12.164Z", "credentialSubject": claimData, } From 6594bfb3c77861778820141d7fe7485ed1fba791 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 19:18:29 +0200 Subject: [PATCH 7/8] feat: compose controller tests --- pkg/restapi/v1/issuer/controller_test.go | 69 ++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/pkg/restapi/v1/issuer/controller_test.go b/pkg/restapi/v1/issuer/controller_test.go index 522fb1777..233ce008c 100644 --- a/pkg/restapi/v1/issuer/controller_test.go +++ b/pkg/restapi/v1/issuer/controller_test.go @@ -817,6 +817,75 @@ func TestController_initiateCredentialIssuance_CompatibilityV1(t *testing.T) { }) } +func TestController_ComposeIssuance(t *testing.T) { + issuerProfile := &profileapi.Issuer{ + OrganizationID: orgID, + ID: profileID, + Version: profileVersion, + Active: true, + OIDCConfig: &profileapi.OIDCConfig{}, + CredentialTemplates: []*profileapi.CredentialTemplate{ + { + ID: "templateID", + }, + }, + } + + var ( + mockProfileSvc = NewMockProfileService(gomock.NewController(t)) + mockOIDC4CISvc = NewMockOIDC4CIService(gomock.NewController(t)) + mockEventSvc = NewMockEventService(gomock.NewController(t)) + c echo.Context + ) + + t.Run("Success", func(t *testing.T) { + expectedCred := map[string]interface{}{ + "a": "b", + } + req, err := json.Marshal(&InitiateOIDC4CIComposeRequest{ + ClientInitiateIssuanceUrl: lo.ToPtr("https://wallet.example.com/initiate_issuance"), + ClientWellknown: lo.ToPtr("https://wallet.example.com/.well-known/openid-configuration"), + Compose: lo.ToPtr([]InitiateIssuanceCredentialConfigurationCompose{ + { + CredentialOverrideId: lo.ToPtr("abc"), + Credential: &expectedCred, + }, + }), + }) + + require.NoError(t, err) + + resp := &oidc4ci.InitiateIssuanceResponse{ + InitiateIssuanceURL: "https://wallet.example.com/initiate_issuance", + TxID: "txID", + } + + mockProfileSvc.EXPECT().GetProfile(profileID, profileVersion).Times(1).Return(issuerProfile, nil) + mockOIDC4CISvc.EXPECT().InitiateIssuance(gomock.Any(), gomock.Any(), issuerProfile). + DoAndReturn(func(ctx context.Context, request *oidc4ci.InitiateIssuanceRequest, issuer *profileapi.Issuer) (*oidc4ci.InitiateIssuanceResponse, error) { + require.Len(t, request.CredentialConfiguration, 1) + require.EqualValues(t, expectedCred, + *request.CredentialConfiguration[0].ComposeCredential.Credential) + + return resp, nil + }) + mockEventSvc.EXPECT().Publish(gomock.Any(), spi.IssuerEventTopic, gomock.Any()).Times(0) + + controller := NewController(&Config{ + ProfileSvc: mockProfileSvc, + OIDC4CIService: mockOIDC4CISvc, + EventSvc: mockEventSvc, + EventTopic: spi.IssuerEventTopic, + Tracer: trace.NewNoopTracerProvider().Tracer(""), + }) + + c = echoContext(withRequestBody(req)) + + err = controller.InitiateCredentialComposeIssuance(c, profileID, profileVersion) + require.NoError(t, err) + }) +} + func TestController_InitiateCredentialIssuance(t *testing.T) { issuerProfile := &profileapi.Issuer{ OrganizationID: orgID, From 18e61ea92657bbc5676b75d13e87eb66855e40ed Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Thu, 23 May 2024 19:23:58 +0200 Subject: [PATCH 8/8] fix: ll --- pkg/restapi/v1/issuer/controller_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/restapi/v1/issuer/controller_test.go b/pkg/restapi/v1/issuer/controller_test.go index 233ce008c..05e143448 100644 --- a/pkg/restapi/v1/issuer/controller_test.go +++ b/pkg/restapi/v1/issuer/controller_test.go @@ -862,7 +862,11 @@ func TestController_ComposeIssuance(t *testing.T) { mockProfileSvc.EXPECT().GetProfile(profileID, profileVersion).Times(1).Return(issuerProfile, nil) mockOIDC4CISvc.EXPECT().InitiateIssuance(gomock.Any(), gomock.Any(), issuerProfile). - DoAndReturn(func(ctx context.Context, request *oidc4ci.InitiateIssuanceRequest, issuer *profileapi.Issuer) (*oidc4ci.InitiateIssuanceResponse, error) { + DoAndReturn(func( + ctx context.Context, + request *oidc4ci.InitiateIssuanceRequest, + issuer *profileapi.Issuer, + ) (*oidc4ci.InitiateIssuanceResponse, error) { require.Len(t, request.CredentialConfiguration, 1) require.EqualValues(t, expectedCred, *request.CredentialConfiguration[0].ComposeCredential.Credential)