From b55132ee3eb42ab9d5740969946646210d6c2edc Mon Sep 17 00:00:00 2001 From: Andrii Holovko Date: Fri, 22 Dec 2023 17:36:00 +0200 Subject: [PATCH] test: refactor bdd tests to use new wallet cli Signed-off-by: Andrii Holovko --- component/wallet-cli/cmd/oidc4vci_cmd.go | 9 +- component/wallet-cli/go.mod | 5 +- .../wallet-cli/pkg/oidc4vci/oidc4vci_flow.go | 76 +-- .../wallet-cli/pkg/oidc4vp/oidc4vp_flow.go | 163 +++---- component/wallet-cli/pkg/wallet/wallet.go | 10 +- .../wallet-cli/pkg/wellknown/wellknown.go | 2 +- .../client_attestation_service.go | 5 + test/bdd/bddtests_test.go | 2 - test/bdd/features/oidc4vc_api.feature | 6 +- .../bdd/features/oidc4vp_multi_vp_api.feature | 26 -- test/bdd/features/oidc4vp_stress.feature | 34 -- test/bdd/go.mod | 10 +- test/bdd/pkg/v1/oidc4vc/models.go | 28 +- .../v1/oidc4vc/{oidc4ci.go => oidc4vci.go} | 441 +++++++++++------- test/bdd/pkg/v1/oidc4vc/oidc4vp.go | 308 +++++++----- test/bdd/pkg/v1/oidc4vc/steps.go | 255 ++++++---- test/bdd/pkg/v1/oidc4vp/oidc4vp.go | 206 -------- test/bdd/pkg/v1/oidc4vp/steps.go | 66 --- test/bdd/pkg/v1/oidc4vp/stress_request.go | 147 ------ test/bdd/pkg/v1/oidc4vp/stress_steps.go | 179 ------- test/bdd/pkg/v1/oidc4vp/wallet.go | 44 -- test/stress/go.mod | 2 +- 22 files changed, 785 insertions(+), 1239 deletions(-) delete mode 100644 test/bdd/features/oidc4vp_multi_vp_api.feature delete mode 100644 test/bdd/features/oidc4vp_stress.feature rename test/bdd/pkg/v1/oidc4vc/{oidc4ci.go => oidc4vci.go} (62%) delete mode 100644 test/bdd/pkg/v1/oidc4vp/oidc4vp.go delete mode 100644 test/bdd/pkg/v1/oidc4vp/steps.go delete mode 100644 test/bdd/pkg/v1/oidc4vp/stress_request.go delete mode 100644 test/bdd/pkg/v1/oidc4vp/stress_steps.go delete mode 100644 test/bdd/pkg/v1/oidc4vp/wallet.go diff --git a/component/wallet-cli/cmd/oidc4vci_cmd.go b/component/wallet-cli/cmd/oidc4vci_cmd.go index d76de5e63..472d119f7 100644 --- a/component/wallet-cli/cmd/oidc4vci_cmd.go +++ b/component/wallet-cli/cmd/oidc4vci_cmd.go @@ -56,7 +56,6 @@ type oidc4vciCommandFlags struct { enableTracing bool proxyURL string trustRegistryURL string - attestationVP string } func NewOIDC4VCICommand() *cobra.Command { @@ -177,9 +176,7 @@ func NewOIDC4VCICommand() *cobra.Command { oidc4vci.WithCredentialType(flags.credentialType), oidc4vci.WithCredentialFormat(flags.credentialFormat), oidc4vci.WithClientID(flags.clientID), - oidc4vci.WithWalletSignatureType(w.SignatureType()), oidc4vci.WithTrustRegistryURL(flags.trustRegistryURL), - oidc4vci.WithAttestationVP(flags.attestationVP), } if walletInitiatedFlow { @@ -196,10 +193,7 @@ func NewOIDC4VCICommand() *cobra.Command { walletDIDIndex = len(w.DIDs()) - 1 } - walledDIDInfo := w.DIDs()[walletDIDIndex] - - opts = append(opts, oidc4vci.WithWalletDID(walledDIDInfo.ID)) - opts = append(opts, oidc4vci.WithWalletKMSKeyID(walledDIDInfo.KeyID)) + opts = append(opts, oidc4vci.WithWalletDIDIndex(walletDIDIndex)) switch flags.grantType { case authorizationCodeGrantType: @@ -307,7 +301,6 @@ func NewOIDC4VCICommand() *cobra.Command { cmd.Flags().StringVar(&flags.pin, "pin", "", "pin for pre-authorized code flow") cmd.Flags().BoolVar(&flags.enableDiscoverableClientID, "enable-discoverable-client-id", false, "enables discoverable client id scheme for dynamic client registration") cmd.Flags().StringVar(&flags.trustRegistryURL, "trust-registry-url", "", "if supplied, wallet will run issuer verification in trust registry") - cmd.Flags().StringVar(&flags.attestationVP, "attestation-vp", "", "wallet attestation vp in jwt format") cmd.Flags().BoolVar(&flags.enableTracing, "enable-tracing", false, "enables http tracing") cmd.Flags().StringVar(&flags.proxyURL, "proxy-url", "", "proxy url for http client") diff --git a/component/wallet-cli/go.mod b/component/wallet-cli/go.mod index 373065ab5..3c7788e01 100644 --- a/component/wallet-cli/go.mod +++ b/component/wallet-cli/go.mod @@ -12,6 +12,7 @@ require ( github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/henvic/httpretty v0.1.0 + github.com/jinzhu/copier v0.3.5 github.com/makiuchi-d/gozxing v0.1.1 github.com/ory/dockertest/v3 v3.9.1 github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f @@ -22,10 +23,12 @@ require ( github.com/syndtr/goleveldb v1.0.0 github.com/trustbloc/did-go v1.0.2-0.20231117120416-ed019bda587f github.com/trustbloc/kms-go v1.0.1-0.20231116141347-14d6bea5727a + github.com/trustbloc/logutil-go v1.0.0-rc1 github.com/trustbloc/vc-go v1.0.3-0.20231117124429-a8a3b24ef734 github.com/trustbloc/vcs v0.0.0-00010101000000-000000000000 github.com/valyala/fastjson v1.6.3 go.mongodb.org/mongo-driver v1.11.4 + go.uber.org/zap v1.23.0 golang.org/x/oauth2 v0.7.0 ) @@ -148,7 +151,6 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/trustbloc/bbs-signature-go v1.0.1 // indirect - github.com/trustbloc/logutil-go v1.0.0-rc1 // indirect github.com/trustbloc/sidetree-go v0.0.0-20231117115139-d71ec9786d12 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect @@ -164,7 +166,6 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.23.0 // indirect golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/mod v0.12.0 // indirect diff --git a/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go b/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go index 3ef7c9a62..22ee1a6cb 100644 --- a/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go +++ b/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go @@ -28,6 +28,7 @@ import ( "github.com/trustbloc/did-go/doc/did" vdrapi "github.com/trustbloc/did-go/vdr/api" "github.com/trustbloc/kms-go/doc/jose" + "github.com/trustbloc/kms-go/spi/kms" "github.com/trustbloc/kms-go/wrapper/api" "github.com/trustbloc/vc-go/jwt" "github.com/trustbloc/vc-go/presexch" @@ -40,7 +41,6 @@ import ( "github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet" "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner/consent" "github.com/trustbloc/vcs/component/wallet-cli/pkg/wellknown" - vcs "github.com/trustbloc/vcs/pkg/doc/verifiable" kmssigner "github.com/trustbloc/vcs/pkg/kms/signer" "github.com/trustbloc/vcs/pkg/restapi/v1/common" issuerv1 "github.com/trustbloc/vcs/pkg/restapi/v1/issuer" @@ -85,6 +85,8 @@ type Flow struct { issuerState string pin string vc *verifiable.Credential + walletKeyID string + walletKeyType kms.KeyType } type provider interface { @@ -129,10 +131,16 @@ func NewFlow(p provider, opts ...Opt) (*Flow, error) { return nil, fmt.Errorf("issuer state not set") } default: - return nil, fmt.Errorf("unsupported flow type: %d", o.flowType) + return nil, fmt.Errorf("unsupported flow type: %s", o.flowType) } - walletDID, err := did.Parse(o.walletDID) + if o.walletDIDIndex < 0 || o.walletDIDIndex >= len(p.Wallet().DIDs()) { + return nil, fmt.Errorf("invalid wallet did index: %d", o.walletDIDIndex) + } + + walletDIDInfo := p.Wallet().DIDs()[o.walletDIDIndex] + + walletDID, err := did.Parse(walletDIDInfo.ID) if err != nil { return nil, fmt.Errorf("parse wallet did: %w", err) } @@ -142,15 +150,17 @@ func NewFlow(p provider, opts ...Opt) (*Flow, error) { return nil, fmt.Errorf("resolve wallet did: %w", err) } - signer, err := p.CryptoSuite().FixedKeyMultiSigner(o.walletKMSKeyID) + signer, err := p.CryptoSuite().FixedKeyMultiSigner(walletDIDInfo.KeyID) if err != nil { - return nil, fmt.Errorf("get signer for key %s: %w", o.walletKMSKeyID, err) + return nil, fmt.Errorf("get signer for key %s: %w", walletDIDInfo.KeyID, err) } + signatureType := p.Wallet().SignatureType() + jwsSigner := jwssigner.NewJWSSigner( docResolution.DIDDocument.VerificationMethod[0].ID, - string(o.walletSignatureType), - kmssigner.NewKMSSigner(signer, o.walletSignatureType, nil), + string(signatureType), + kmssigner.NewKMSSigner(signer, signatureType, nil), ) return &Flow{ @@ -160,6 +170,8 @@ func NewFlow(p provider, opts ...Opt) (*Flow, error) { signer: jwsSigner, wallet: p.Wallet(), wellKnownService: p.WellKnownService(), + walletKeyID: walletDIDInfo.KeyID, + walletKeyType: walletDIDInfo.KeyType, flowType: o.flowType, credentialOffer: o.credentialOffer, credentialType: o.credentialType, @@ -181,7 +193,7 @@ func (f *Flow) GetVC() *verifiable.Credential { } func (f *Flow) Run(ctx context.Context) error { - slog.Info("running OIDC4VCI flow", + slog.Info("Running OIDC4VCI flow", "flow_type", f.flowType, "credential_offer_uri", f.credentialOffer, "credential_type", f.credentialType, @@ -230,7 +242,7 @@ func (f *Flow) Run(ctx context.Context) error { return fmt.Errorf("credential offer is empty") } - slog.Info("validate issuer", "url", f.trustRegistryURL) + slog.Info("Validating issuer", "url", f.trustRegistryURL) credentialOffer := credentialOfferResponse.Credentials[0] @@ -277,12 +289,14 @@ func (f *Flow) Run(ctx context.Context) error { return err } + ctx = context.WithValue(ctx, oauth2.HTTPClient, f.httpClient) + token, err = f.exchangeAuthorizationCodeForAccessToken(ctx, oauthClient, authCode) if err != nil { return err } } else if f.flowType == FlowTypePreAuthorizedCode { - slog.Info("getting access token", + slog.Info("Getting access token", "grant_type", preAuthorizedCodeGrantType, "client_id", f.clientID, "pre-authorized_code", preAuthorizationGrant.PreAuthorizedCode, @@ -368,7 +382,7 @@ func (f *Flow) Run(ctx context.Context) error { } func (f *Flow) parseCredentialOfferURI(uri string) (*oidc4ci.CredentialOfferResponse, error) { - slog.Info("parsing credential offer URI", + slog.Info("Parsing credential offer URI", "uri", uri, ) @@ -386,7 +400,7 @@ func (f *Flow) parseCredentialOfferURI(uri string) (*oidc4ci.CredentialOfferResp } func (f *Flow) getAuthorizationCode(oauthClient *oauth2.Config, issuerState string) (string, error) { - slog.Info("getting authorization code", + slog.Info("Getting authorization code", "client_id", oauthClient.ClientID, "scopes", oauthClient.Scopes, "redirect_uri", oauthClient.RedirectURL, @@ -542,7 +556,7 @@ func (f *Flow) exchangeAuthorizationCodeForAccessToken( oauthClient *oauth2.Config, authCode string, ) (*oauth2.Token, error) { - slog.Info("exchanging authorization code for access token", + slog.Info("Exchanging authorization code for access token", "grant_type", "authorization_code", "client_id", oauthClient.ClientID, "auth_code", authCode, @@ -610,6 +624,8 @@ func (f *Flow) getAttestationVP() (string, error) { return "", fmt.Errorf("create vp: %w", err) } + attestationVP.ID = uuid.New().String() + claims, err := attestationVP.JWTClaims([]string{}, false) if err != nil { return "", fmt.Errorf("get attestation claims: %w", err) @@ -638,7 +654,8 @@ func (f *Flow) getVC( credentialIssuer string, ) (*verifiable.Credential, error) { credentialEndpoint := wellKnown.CredentialEndpoint - slog.Info("getting credential", + + slog.Info("Getting credential", "credential_endpoint", credentialEndpoint, "credential_issuer", credentialIssuer, ) @@ -726,6 +743,10 @@ func (f *Flow) getVC( return nil, fmt.Errorf("parse credential: %w", err) } + slog.Info("Credential received", + "vc", string(vcBytes), + ) + if err = f.handleIssuanceAck(wellKnown, &credentialResp, token); err != nil { return nil, err } @@ -841,11 +862,8 @@ type options struct { userPassword string issuerState string pin string - walletDID string - walletKMSKeyID string - walletSignatureType vcs.SignatureType trustRegistryURL string - attestationVP string + walletDIDIndex int } type Opt func(opts *options) @@ -922,32 +940,14 @@ func WithPin(pin string) Opt { } } -func WithWalletDID(walletDID string) Opt { - return func(opts *options) { - opts.walletDID = walletDID - } -} - -func WithWalletKMSKeyID(keyID string) Opt { - return func(opts *options) { - opts.walletKMSKeyID = keyID - } -} - -func WithWalletSignatureType(walletSignatureType vcs.SignatureType) Opt { - return func(opts *options) { - opts.walletSignatureType = walletSignatureType - } -} - func WithTrustRegistryURL(url string) Opt { return func(opts *options) { opts.trustRegistryURL = url } } -func WithAttestationVP(jwtVP string) Opt { +func WithWalletDIDIndex(idx int) Opt { return func(opts *options) { - opts.attestationVP = jwtVP + opts.walletDIDIndex = idx } } diff --git a/component/wallet-cli/pkg/oidc4vp/oidc4vp_flow.go b/component/wallet-cli/pkg/oidc4vp/oidc4vp_flow.go index 4ef0cae6a..a482b640e 100644 --- a/component/wallet-cli/pkg/oidc4vp/oidc4vp_flow.go +++ b/component/wallet-cli/pkg/oidc4vp/oidc4vp_flow.go @@ -19,10 +19,12 @@ import ( "time" "github.com/google/uuid" + "github.com/jinzhu/copier" "github.com/piprate/json-gold/ld" "github.com/trustbloc/did-go/doc/did" vdrapi "github.com/trustbloc/did-go/vdr/api" "github.com/trustbloc/kms-go/doc/jose" + "github.com/trustbloc/kms-go/spi/kms" "github.com/trustbloc/kms-go/wrapper/api" didconfigclient "github.com/trustbloc/vc-go/didconfig/client" "github.com/trustbloc/vc-go/jwt" @@ -62,6 +64,7 @@ type Flow struct { requestURI string enableLinkedDomainVerification bool disableDomainMatching bool + disableSchemaValidation bool trustRegistryURL string } @@ -126,11 +129,21 @@ func NewFlow(p provider, opts ...Opt) (*Flow, error) { requestURI: o.requestURI, enableLinkedDomainVerification: o.enableLinkedDomainVerification, disableDomainMatching: o.disableDomainMatching, + disableSchemaValidation: o.disableSchemaValidation, trustRegistryURL: o.trustRegistryURL, }, nil } func (f *Flow) Run(ctx context.Context) error { + slog.Info("Running OIDC4VP flow", + "wallet_did", f.walletDID.String(), + "request_uri", f.requestURI, + "enable_linked_domain_verification", f.enableLinkedDomainVerification, + "disable_domain_matching", f.disableDomainMatching, + "disable_schema_validation", f.disableSchemaValidation, + "trust_registry_url", f.trustRegistryURL, + ) + requestObject, err := f.fetchRequestObject(ctx) if err != nil { return err @@ -142,7 +155,22 @@ func (f *Flow) Run(ctx context.Context) error { } } - credentials, err := f.queryWallet(requestObject.Claims.VPToken.PresentationDefinition) + var pd presexch.PresentationDefinition + + if err = copier.CopyWithOption( + &pd, + requestObject.Claims.VPToken.PresentationDefinition, + copier.Option{IgnoreEmpty: true, DeepCopy: true}, + ); err != nil { + return fmt.Errorf("copy presentation definition: %w", err) + } + + if f.disableSchemaValidation && len(pd.InputDescriptors) > 0 { + pd.InputDescriptors[0].Schema = nil + requestObject.Claims.VPToken.PresentationDefinition.InputDescriptors[0].Schema = nil + } + + vp, err := f.queryWallet(&pd) if err != nil { return fmt.Errorf("query wallet: %w", err) } @@ -154,13 +182,15 @@ func (f *Flow) Run(ctx context.Context) error { ValidateVerifier( requestObject.ClientID, "", - credentials, + vp.Credentials(), ); err != nil { return fmt.Errorf("validate verifier: %w", err) } } if !f.disableDomainMatching { + credentials := vp.Credentials() + for i := len(credentials) - 1; i >= 0; i-- { credential := credentials[i] if !sameDIDWebDomain(credential.Contents().Issuer.ID, requestObject.ClientID) { @@ -169,7 +199,7 @@ func (f *Flow) Run(ctx context.Context) error { } } - if err = f.sendAuthorizationResponse(ctx, requestObject, credentials); err != nil { + if err = f.sendAuthorizationResponse(ctx, requestObject, vp); err != nil { return fmt.Errorf("send authorization response: %w", err) } @@ -177,7 +207,7 @@ func (f *Flow) Run(ctx context.Context) error { } func (f *Flow) fetchRequestObject(ctx context.Context) (*RequestObject, error) { - slog.Info("fetching request object", + slog.Info("Fetching request object", "uri", f.requestURI, ) @@ -236,7 +266,7 @@ type serviceEndpoint struct { } func (f *Flow) runLinkedDomainVerification(clientDID string) error { - slog.Info("running linked domain verification", + slog.Info("Running linked domain verification", "did", clientDID, ) @@ -299,8 +329,8 @@ func getServiceType(serviceType interface{}) string { return val } -func (f *Flow) queryWallet(pd *presexch.PresentationDefinition) ([]*verifiable.Credential, error) { - slog.Info("querying wallet") +func (f *Flow) queryWallet(pd *presexch.PresentationDefinition) (*verifiable.Presentation, error) { + slog.Info("Querying wallet") b, err := json.Marshal(pd) if err != nil { @@ -316,7 +346,7 @@ func (f *Flow) queryWallet(pd *presexch.PresentationDefinition) ([]*verifiable.C return nil, fmt.Errorf("no matching credentials found") } - return presentations[0].Credentials(), nil + return presentations[0], nil } func sameDIDWebDomain(did1, did2 string) bool { @@ -334,99 +364,30 @@ func sameDIDWebDomain(did1, did2 string) bool { func (f *Flow) sendAuthorizationResponse( ctx context.Context, requestObject *RequestObject, - credentials []*verifiable.Credential, + vp *verifiable.Presentation, ) error { - slog.Info("sending authorization response", + slog.Info("Sending authorization response", "redirect_uri", requestObject.RedirectURI, ) - presentationDefinition := requestObject.Claims.VPToken.PresentationDefinition - - var ( - vpToken string - presentationSubmission *presexch.PresentationSubmission - ) - - if len(credentials) == 1 { - credential := credentials[0] - - presentation, err := presentationDefinition.CreateVP( - []*verifiable.Credential{credential}, - f.documentLoader, - presexch.WithSDCredentialOptions( - verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(f.documentLoader), - ), - ) - if err != nil { - return fmt.Errorf("create vp: %w", err) - } - - var ok bool - - presentationSubmission, ok = presentation.CustomFields["presentation_submission"].(*presexch.PresentationSubmission) - if !ok { - return fmt.Errorf("missing or invalid presentation_submission") - } + presentationSubmission, ok := vp.CustomFields["presentation_submission"].(*presexch.PresentationSubmission) + if !ok { + return fmt.Errorf("missing or invalid presentation_submission") + } - vpFormats := requestObject.Registration.VPFormats + vpFormats := requestObject.Registration.VPFormats + for i := range presentationSubmission.DescriptorMap { if vpFormats.JwtVP != nil { - presentationSubmission.DescriptorMap[0].Format = "jwt_vp" + presentationSubmission.DescriptorMap[i].Format = "jwt_vp" } else if vpFormats.LdpVP != nil { - presentationSubmission.DescriptorMap[0].Format = "ldp_vp" - } - - vpToken, err = f.createVPToken(presentation, requestObject) - if err != nil { - return fmt.Errorf("create vp token: %w", err) - } - } else if len(credentials) > 1 { - var ( - vpTokens []string - presentations []*verifiable.Presentation - err error - ) - - presentations, presentationSubmission, err = presentationDefinition.CreateVPArray( - credentials, - f.documentLoader, - presexch.WithSDCredentialOptions( - verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(f.documentLoader), - ), - ) - if err != nil { - return fmt.Errorf("create vp array: %w", err) - } - - for i, presentation := range presentations { - delete(presentation.CustomFields, "presentation_submission") - - vpFormats := requestObject.Registration.VPFormats - - if vpFormats.JwtVP != nil { - presentationSubmission.DescriptorMap[i].Format = "jwt_vp" - } else if vpFormats.LdpVP != nil { - presentationSubmission.DescriptorMap[i].Format = "ldp_vp" - } - - token, createErr := f.createVPToken(presentation, requestObject) - if createErr != nil { - return fmt.Errorf("create vp token: %w", createErr) - } - - vpTokens = append(vpTokens, token) - } - - b, err := json.Marshal(vpTokens) - if err != nil { - return fmt.Errorf("marshal vp tokens: %w", err) + presentationSubmission.DescriptorMap[i].Format = "ldp_vp" } + } - vpToken = string(b) - } else { - return fmt.Errorf("no matching credentials found") + vpToken, err := f.createVPToken(vp, requestObject) + if err != nil { + return fmt.Errorf("create vp token: %w", err) } idToken, err := f.createIDToken(presentationSubmission, requestObject.ClientID, requestObject.Nonce, requestObject.Scope) @@ -552,11 +513,15 @@ func (f *Flow) signPresentationLDP( verificationMethod := docResolution.DIDDocument.VerificationMethod[0] - var kmsKeyID string + var ( + kmsKeyID string + kmsKeyType kms.KeyType + ) for _, didInfo := range f.wallet.DIDs() { if didInfo.ID == signerDID { kmsKeyID = didInfo.KeyID + kmsKeyType = didInfo.KeyType break } } @@ -564,6 +529,7 @@ func (f *Flow) signPresentationLDP( signedVP, err := cryptoSigner.SignPresentation( &vc.Signer{ Creator: verificationMethod.ID, + KeyType: kmsKeyType, KMSKeyID: kmsKeyID, SignatureType: signatureType, SignatureRepresentation: verifiable.SignatureProofValue, @@ -629,6 +595,10 @@ func (f *Flow) createIDToken( } func (f *Flow) postAuthorizationResponse(ctx context.Context, redirectURI string, body []byte) error { + slog.Info("Sending authorization response", + "redirect_uri", redirectURI, + ) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, redirectURI, bytes.NewBuffer(body)) if err != nil { return fmt.Errorf("new authorization response request: %w", err) @@ -661,7 +631,7 @@ func (f *Flow) postAuthorizationResponse(ctx context.Context, redirectURI string ) } - slog.Info("credential presented successfully") + slog.Info("Credential presented successfully") return nil } @@ -671,6 +641,7 @@ type options struct { requestURI string enableLinkedDomainVerification bool disableDomainMatching bool + disableSchemaValidation bool trustRegistryURL string } @@ -700,6 +671,12 @@ func WithDomainMatchingDisabled() Opt { } } +func WithSchemaValidationDisabled() Opt { + return func(opts *options) { + opts.disableSchemaValidation = true + } +} + func WithTrustRegistryURL(url string) Opt { return func(opts *options) { opts.trustRegistryURL = url diff --git a/component/wallet-cli/pkg/wallet/wallet.go b/component/wallet-cli/pkg/wallet/wallet.go index 2ee41fdfb..46785a85a 100644 --- a/component/wallet-cli/pkg/wallet/wallet.go +++ b/component/wallet-cli/pkg/wallet/wallet.go @@ -38,8 +38,9 @@ const ( ) type DIDInfo struct { - ID string `json:"id"` - KeyID string `json:"key_id"` + ID string `json:"id"` + KeyID string `json:"key_id"` + KeyType kmsapi.KeyType `json:"key_type"` } type Wallet struct { @@ -148,8 +149,9 @@ func New(p provider, opts ...Opt) (*Wallet, error) { } dids = append(dids, &DIDInfo{ - ID: res.DidID, - KeyID: strings.Split(res.KeyID, "#")[1], + ID: res.DidID, + KeyID: strings.Split(res.KeyID, "#")[1], + KeyType: keyType, }) updateDIDs = true diff --git a/component/wallet-cli/pkg/wellknown/wellknown.go b/component/wallet-cli/pkg/wellknown/wellknown.go index efac06687..cd44a87b7 100644 --- a/component/wallet-cli/pkg/wellknown/wellknown.go +++ b/component/wallet-cli/pkg/wellknown/wellknown.go @@ -31,7 +31,7 @@ type Service struct { func (s *Service) GetWellKnownOpenIDConfiguration( issuerURL string, ) (*issuerv1.WellKnownOpenIDIssuerConfiguration, error) { - slog.Info("getting OpenID credential issuer configuration", + slog.Info("Getting OpenID credential issuer configuration", "url", issuerURL+"/.well-known/openid-credential-issuer", ) diff --git a/pkg/service/clientattestation/client_attestation_service.go b/pkg/service/clientattestation/client_attestation_service.go index 7aa7e8836..27ceae6f0 100644 --- a/pkg/service/clientattestation/client_attestation_service.go +++ b/pkg/service/clientattestation/client_attestation_service.go @@ -274,6 +274,11 @@ func (s *Service) requestPolicyEvaluation( policyURL string, payload []byte, ) (*PolicyEvaluationResponse, error) { + logger.Debugc(ctx, "request policy evaluation", + zap.String("url", policyURL), + zap.String("payload", string(payload)), + ) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, policyURL, bytes.NewReader(payload)) if err != nil { return nil, fmt.Errorf("create request: %w", err) diff --git a/test/bdd/bddtests_test.go b/test/bdd/bddtests_test.go index b6cc5e175..127cc6765 100644 --- a/test/bdd/bddtests_test.go +++ b/test/bdd/bddtests_test.go @@ -23,7 +23,6 @@ import ( "github.com/trustbloc/vcs/test/bdd/pkg/common" bddctx "github.com/trustbloc/vcs/test/bdd/pkg/context" "github.com/trustbloc/vcs/test/bdd/pkg/v1/oidc4vc" - "github.com/trustbloc/vcs/test/bdd/pkg/v1/oidc4vp" vcv1 "github.com/trustbloc/vcs/test/bdd/pkg/v1/vc" vc_devapi "github.com/trustbloc/vcs/test/bdd/pkg/vc-devapi" vc_echo "github.com/trustbloc/vcs/test/bdd/pkg/vc-echo" @@ -155,7 +154,6 @@ func InitializeScenario(sc *godog.ScenarioContext) { common.NewSteps(bddContext), vcv1.NewSteps(bddContext), oidc4vcSteps, - oidc4vp.NewSteps(bddContext), vc_echo.NewSteps(bddContext), vc_devapi.NewSteps(bddContext), vc_version.NewSteps(bddContext), diff --git a/test/bdd/features/oidc4vc_api.feature b/test/bdd/features/oidc4vc_api.feature index e2cc4758b..696b824c5 100644 --- a/test/bdd/features/oidc4vc_api.feature +++ b/test/bdd/features/oidc4vc_api.feature @@ -186,7 +186,7 @@ Feature: OIDC4VC REST API When User interacts with Wallet to initiate credential issuance using pre authorization code flow Then credential is issued - And User interacts with Verifier and initiate OIDC4VP interaction under "" profile with presentation definition ID "" and fields "" and receives "query vc using presentation definition: no result found" error + And User interacts with Verifier and initiate OIDC4VP interaction under "" profile with presentation definition ID "" and fields "" and receives "no matching credentials found" error Then we wait 15 seconds And Verifier with profile "" requests expired interactions claims @@ -205,14 +205,14 @@ Feature: OIDC4VC REST API When User interacts with Wallet to initiate credential issuance using pre authorization code flow Then credential is issued And wallet configured to use hardcoded vp_token format "jwt" for OIDC4VP interaction - And User interacts with Verifier and initiate OIDC4VP interaction under "" profile with presentation definition ID "" and fields "" and receives "profile does not support jwt vp_token format" error + And User interacts with Verifier and initiate OIDC4VP interaction under "" profile with presentation definition ID "" and fields "" and receives "no matching credentials found" error Then we wait 15 seconds And Verifier with profile "" requests expired interactions claims Examples: | issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields | # LDP issuer, LDP verifier, no limit disclosure and schema match in PD query. - | i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id | + | i_myprofile_ud_es256k_jwt/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address | Scenario Outline: OIDC credential issuance without required role diff --git a/test/bdd/features/oidc4vp_multi_vp_api.feature b/test/bdd/features/oidc4vp_multi_vp_api.feature deleted file mode 100644 index 65e435f5e..000000000 --- a/test/bdd/features/oidc4vp_multi_vp_api.feature +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright SecureKey Technologies Inc. All Rights Reserved. -# -# SPDX-License-Identifier: Apache-2.0 -# - -@all -@oidc4_multi_vp_rest -Feature: Using OIDC4VP REST API - - Background: - Given User creates wallet with 3 DID - And New verifiable credentials is created from table: - | IssuerProfile | UserName | Password | Credential | DIDIndex | - | i_myprofile_cp_p384/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | crude_product.json | 0 | - | i_myprofile_ud_es256k_jwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | permanent_resident_card.json | 1 | - | i_myprofile_ud_es384_sdjwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | 2 | - And User saves credentials into wallet - - Scenario: Initiate, check authorization response for jwt verifier - Given Profile "v_myprofile_multivp_jwt/v1.0" verifier has been authorized with username "profile-user-verifier-1" and password "profile-user-verifier-1-pwd" - And OIDC4VP interaction initiated under "v_myprofile_multivp_jwt/v1.0" profile - And Wallet verify authorization request and decode claims - And Wallet looks for credential that match authorization multi VP - And Wallet send authorization response - And Verifier with profile "v_myprofile_multivp_jwt/v1.0" requests interactions claims \ No newline at end of file diff --git a/test/bdd/features/oidc4vp_stress.feature b/test/bdd/features/oidc4vp_stress.feature deleted file mode 100644 index f597a56b1..000000000 --- a/test/bdd/features/oidc4vp_stress.feature +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright SecureKey Technologies Inc. All Rights Reserved. -# -# SPDX-License-Identifier: Apache-2.0 -# - -@oidc4vp_stress -Feature: Using OIDC4VP REST API - - Background: - When User creates wallet with 1 DID - And With AccessTokenUrlEnv "ACCESS_TOKEN_URL", new verifiable credentials is created from table: - | IssuerProfile | UserName | Password |Credential | VCFormat | - | i_myprofile_ud_P256k1/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | ldp_vc | - | i_myprofile_ud_p256/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | ldp_vc | - | i_myprofile_prc_P256k1/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | permanent_resident_card.json | ldp_vc | - | i_myprofile_prc_p256/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | permanent_resident_card.json | ldp_vc | - | i_myprofile_cp_p384/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | crude_product.json | ldp_vc | - | i_myprofile_cp_p256/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | crude_product.json | ldp_vc | - | i_myprofile_cmtr_p384/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | certified_mill_test_report.json | ldp_vc | - | i_myprofile_cmtr_p256_ldp/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | certified_mill_test_report.json | ldp_vc | - | i_myprofile_ud_es256_jwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | jwt_vc_json-ld | - | i_myprofile_ud_es384_jwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | jwt_vc_json-ld | - | i_myprofile_ud_es256k_jwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | jwt_vc_json-ld | - | i_myprofile_ud_es256_sdjwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | jwt_vc_json-ld | - | i_myprofile_ud_es384_sdjwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | jwt_vc_json-ld | - | i_myprofile_ud_es256k_sdjwt/v1.0 | profile-user-issuer-1 | profile-user-issuer-1-pwd | university_degree.json | jwt_vc_json-ld | - And User saves credentials into wallet - And Profile "v_myprofile_jwt/v1.0" verifier has been authorized with username "profile-user-verifier-1" and password "profile-user-verifier-1-pwd" - - @e2e - - Scenario: Stress test method - And "USER_NUMS" users execute oidc4vp flow with init "INIT_INTERACTION_URL" url, with retrieve "RETRIEVE_CLAIMS_URL" url, for verify profile "VERIFY_PROFILE_ID" using "CONCURRENT_REQ" concurrent requests \ No newline at end of file diff --git a/test/bdd/go.mod b/test/bdd/go.mod index f7db2d325..44eb2643b 100644 --- a/test/bdd/go.mod +++ b/test/bdd/go.mod @@ -17,16 +17,17 @@ require ( github.com/jedib0t/go-pretty/v6 v6.4.6 github.com/labstack/echo/v4 v4.11.1 github.com/ory/fosite v0.44.0 + github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f github.com/rdumont/assistdog v0.0.0-20201106100018-168b06230d14 github.com/samber/lo v1.38.1 - github.com/stretchr/testify v1.8.4 github.com/tidwall/gjson v1.14.4 github.com/trustbloc/cmdutil-go v0.0.0-20221125151303-09d42adcc811 github.com/trustbloc/did-go v1.0.2-0.20231117120416-ed019bda587f + github.com/trustbloc/kms-go v1.0.1-0.20231116141347-14d6bea5727a github.com/trustbloc/logutil-go v1.0.0-rc1 github.com/trustbloc/vc-go v1.0.3-0.20231117124429-a8a3b24ef734 github.com/trustbloc/vcs v0.1.9-0.20230210204445-f2870a36f0ea - github.com/trustbloc/vcs/component/wallet-cli v0.0.0-20231003142611-b9399c5814c2 + github.com/trustbloc/vcs/component/wallet-cli v0.0.0-20231222131742-742a7ae591ba github.com/trustbloc/vcs/test/stress v0.0.0-00010101000000-000000000000 go.uber.org/zap v1.23.0 golang.org/x/oauth2 v0.7.0 @@ -62,7 +63,6 @@ require ( github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect github.com/cucumber/messages-go/v16 v16.0.1 // indirect github.com/dave/jennifer v1.6.1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/deepmap/oapi-codegen v1.11.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect @@ -94,6 +94,7 @@ require ( github.com/henvic/httpretty v0.1.0 // indirect github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jinzhu/copier v0.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e // indirect github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect @@ -123,9 +124,7 @@ require ( github.com/ory/x v0.0.573 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.0.9 // indirect - github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -145,7 +144,6 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/trustbloc/bbs-signature-go v1.0.1 // indirect - github.com/trustbloc/kms-go v1.0.1-0.20231116141347-14d6bea5727a // indirect github.com/trustbloc/sidetree-go v0.0.0-20231117115139-d71ec9786d12 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fastjson v1.6.3 // indirect diff --git a/test/bdd/pkg/v1/oidc4vc/models.go b/test/bdd/pkg/v1/oidc4vc/models.go index fdc5ed59c..ff457dae7 100644 --- a/test/bdd/pkg/v1/oidc4vc/models.go +++ b/test/bdd/pkg/v1/oidc4vc/models.go @@ -8,10 +8,11 @@ package oidc4vc import ( util "github.com/trustbloc/did-go/doc/util/time" + vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable" ) -type initiateOIDC4CIRequest struct { +type initiateOIDC4VCIRequest struct { ClaimData *map[string]interface{} `json:"claim_data,omitempty"` ClaimEndpoint string `json:"claim_endpoint,omitempty"` ClientInitiateIssuanceUrl string `json:"client_initiate_issuance_url,omitempty"` @@ -24,12 +25,27 @@ type initiateOIDC4CIRequest struct { UserPinRequired bool `json:"user_pin_required,omitempty"` } -type initiateOIDC4CIResponse struct { +type initiateOIDC4VCIResponse struct { OfferCredentialURL string `json:"offer_credential_url"` TxId string `json:"tx_id"` UserPin *string `json:"user_pin"` } +type initiateOIDC4VPRequest struct { + PresentationDefinitionId string `json:"presentationDefinitionId,omitempty"` + PresentationDefinitionFilters *presentationDefinitionFilters `json:"presentationDefinitionFilters,omitempty"` + Scopes []string `json:"scopes,omitempty"` +} + +type presentationDefinitionFilters struct { + Fields *[]string `json:"fields,omitempty"` +} + +type initiateOIDC4VPResponse struct { + AuthorizationRequest string `json:"authorizationRequest"` + TxId string `json:"txID"` +} + type clientRegistrationRequest struct { ClientName *string `json:"client_name,omitempty"` ClientUri *string `json:"client_uri,omitempty"` @@ -98,12 +114,14 @@ type credentialIssuanceHistoryData struct { TransactionId string `json:"transaction_id,omitempty"` } -type retrievedCredentialsClaims struct { +type credentialMetadata struct { Format vcsverifiable.Format `json:"format,omitempty"` Type []string `json:"type,omitempty"` - SubjectData []map[string]interface{} `json:"subjectData,omitempty"` - Issuer map[string]interface{} `json:"issuer,omitempty"` + SubjectData interface{} `json:"subjectData,omitempty"` + Issuer interface{} `json:"issuer,omitempty"` IssuanceDate *util.TimeWrapper `json:"issuanceDate,omitempty"` ExpirationDate *util.TimeWrapper `json:"expirationDate,omitempty"` CustomClaims map[string]map[string]interface{} `json:"customClaims,omitempty"` } + +type retrievedCredentialClaims map[string]credentialMetadata diff --git a/test/bdd/pkg/v1/oidc4vc/oidc4ci.go b/test/bdd/pkg/v1/oidc4vc/oidc4vci.go similarity index 62% rename from test/bdd/pkg/v1/oidc4vc/oidc4ci.go rename to test/bdd/pkg/v1/oidc4vc/oidc4vci.go index eb67982cb..dc0437f23 100644 --- a/test/bdd/pkg/v1/oidc4vc/oidc4ci.go +++ b/test/bdd/pkg/v1/oidc4vc/oidc4vci.go @@ -22,13 +22,19 @@ import ( "github.com/google/uuid" "github.com/ory/fosite" + "github.com/piprate/json-gold/ld" "github.com/samber/lo" utiltime "github.com/trustbloc/did-go/doc/util/time" + vdrapi "github.com/trustbloc/did-go/vdr/api" + storageapi "github.com/trustbloc/kms-go/spi/storage" + "github.com/trustbloc/kms-go/wrapper/api" "github.com/trustbloc/vc-go/verifiable" "golang.org/x/oauth2" - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner" + "github.com/trustbloc/vcs/component/wallet-cli/pkg/oidc4vci" + "github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet" "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner/vcprovider" + "github.com/trustbloc/vcs/component/wallet-cli/pkg/wellknown" vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable" "github.com/trustbloc/vcs/test/bdd/pkg/bddutil" "github.com/trustbloc/vcs/test/bdd/pkg/v1/model" @@ -48,32 +54,32 @@ func (s *Steps) authorizeIssuerProfileUser(profileVersionedID, username, passwor if err := s.ResetAndSetup(); err != nil { return err } - issuerProfile, ok := s.bddContext.IssuerProfiles[profileVersionedID] + issuerProfile, ok := s.bddContext.IssuerProfiles[profileVersionedID] if !ok { return fmt.Errorf("issuer profile '%s' not found", profileVersionedID) } - accessToken, err := bddutil.IssueAccessToken(context.Background(), oidcProviderURL, - username, password, []string{"org_admin"}) + accessToken, err := bddutil.IssueAccessToken(context.Background(), oidcProviderURL, username, password, + []string{"org_admin"}) if err != nil { return err } s.bddContext.Args[getOrgAuthTokenKey(issuerProfile.ID+"/"+issuerProfile.Version)] = accessToken - s.issuerProfile = issuerProfile + return nil } -func (s *Steps) initiateCredentialIssuance(initiateOIDC4CIRequest initiateOIDC4CIRequest) (*initiateOIDC4CIResponse, error) { +func (s *Steps) initiateCredentialIssuance(req initiateOIDC4VCIRequest) (*initiateOIDC4VCIResponse, error) { endpointURL := fmt.Sprintf(initiateCredentialIssuanceURLFormat, s.issuerProfile.ID, s.issuerProfile.Version) token := s.bddContext.Args[getOrgAuthTokenKey(s.issuerProfile.ID+"/"+s.issuerProfile.Version)] - reqBody, err := json.Marshal(initiateOIDC4CIRequest) + reqBody, err := json.Marshal(req) if err != nil { - return nil, fmt.Errorf("marshal initiate oidc4vc req: %w", err) + return nil, fmt.Errorf("marshal initiate oidc4vci req: %w", err) } resp, err := bddutil.HTTPSDo(http.MethodPost, endpointURL, "application/json", token, bytes.NewReader(reqBody), @@ -93,10 +99,10 @@ func (s *Steps) initiateCredentialIssuance(initiateOIDC4CIRequest initiateOIDC4C return nil, bddutil.ExpectedStatusCodeError(http.StatusOK, resp.StatusCode, b) } - var r *initiateOIDC4CIResponse + var r *initiateOIDC4VCIResponse if err = json.Unmarshal(b, &r); err != nil { - return nil, fmt.Errorf("unmarshal initiate oidc4vc resp: %w", err) + return nil, fmt.Errorf("unmarshal initiate oidc4vci resp: %w", err) } if err = s.checkInitiateIssuanceURL(r.OfferCredentialURL); err != nil { @@ -118,28 +124,41 @@ func (s *Steps) checkInitiateIssuanceURL(initiateIssuanceURL string) error { return nil } -func (s *Steps) runOIDC4CIPreAuth(initiateOIDC4CIRequest initiateOIDC4CIRequest) error { +func (s *Steps) runOIDC4VCIPreAuth(initiateOIDC4CIRequest initiateOIDC4VCIRequest) error { initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(initiateOIDC4CIRequest) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("init credential issuance: %w", err) + } + + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, + oidc4vci.WithFlowType(oidc4vci.FlowTypePreAuthorizedCode), + oidc4vci.WithCredentialOffer(initiateOIDC4CIResponseData.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithPin(*initiateOIDC4CIResponseData.UserPin), + ) + if err != nil { + return fmt.Errorf("init pre-auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err != nil { + return fmt.Errorf("run pre-auth flow: %w", err) } - _, err = s.walletRunner.RunOIDC4CIPreAuth( - &walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Pin: *initiateOIDC4CIResponseData.UserPin, - }, nil) + vcBytes, err := json.Marshal(flow.GetVC()) if err != nil { - return fmt.Errorf("s.walletRunner.RunOIDC4CIPreAuth: %w", err) + return fmt.Errorf("marshal vc: %w", err) + } + + if err = s.wallet.Add(vcBytes); err != nil { + return fmt.Errorf("add vc to wallet: %w", err) } return nil } -func (s *Steps) runOIDC4CIPreAuthWithInvalidClaims() error { - initiateIssuanceRequest := initiateOIDC4CIRequest{ +func (s *Steps) runOIDC4VCIPreAuthWithInvalidClaims() error { + initiateIssuanceRequest := initiateOIDC4VCIRequest{ CredentialTemplateId: "universityDegreeTemplateID", ClaimData: &map[string]interface{}{ "degree": map[string]string{ @@ -153,7 +172,7 @@ func (s *Steps) runOIDC4CIPreAuthWithInvalidClaims() error { UserPinRequired: true, } - err := s.runOIDC4CIPreAuth(initiateIssuanceRequest) + err := s.runOIDC4VCIPreAuth(initiateIssuanceRequest) if err == nil { return errors.New("error expected") } @@ -166,7 +185,7 @@ func (s *Steps) runOIDC4CIPreAuthWithInvalidClaims() error { } func (s *Steps) initiateCredentialIssuanceWithClaimsSchemaValidationError() error { - initiateIssuanceRequest := initiateOIDC4CIRequest{ + initiateIssuanceRequest := initiateOIDC4VCIRequest{ CredentialTemplateId: "universityDegreeTemplateID", ClaimData: &map[string]interface{}{ "degree": map[string]string{ @@ -223,20 +242,16 @@ func (s *Steps) runOIDC4CIPreAuthWithValidClaims() error { return fmt.Errorf("fetchClaimData: %w", err) } - initiateIssuanceRequest := initiateOIDC4CIRequest{ + initiateIssuanceRequest := initiateOIDC4VCIRequest{ CredentialTemplateId: s.issuedCredentialTemplateID, ClaimData: &claims, UserPinRequired: true, } - return s.runOIDC4CIPreAuth(initiateIssuanceRequest) + return s.runOIDC4VCIPreAuth(initiateIssuanceRequest) } func (s *Steps) runOIDC4CIPreAuthWithClientAttestation() error { - if err := s.walletRunner.CreateWallet(); err != nil { - return fmt.Errorf("create wallet: %w", err) - } - if err := s.addAttestationVC(); err != nil { return fmt.Errorf("add attestation vc to wallet: %w", err) } @@ -246,27 +261,39 @@ func (s *Steps) runOIDC4CIPreAuthWithClientAttestation() error { return fmt.Errorf("fetchClaimData: %w", err) } - initiateIssuanceRequest := initiateOIDC4CIRequest{ + req := initiateOIDC4VCIRequest{ CredentialTemplateId: s.issuedCredentialTemplateID, ClaimData: &claims, UserPinRequired: true, } - initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(initiateIssuanceRequest) + initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(req) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("initiate credential issuance: %w", err) } - _, err = s.walletRunner.RunOIDC4CIPreAuth( - &walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Pin: *initiateOIDC4CIResponseData.UserPin, - EnableClientAttestation: true, - }, nil) + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, + oidc4vci.WithFlowType(oidc4vci.FlowTypePreAuthorizedCode), + oidc4vci.WithCredentialOffer(initiateOIDC4CIResponseData.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithPin(*initiateOIDC4CIResponseData.UserPin), + ) if err != nil { - return fmt.Errorf("s.walletRunner.RunOIDC4CIPreAuth: %w", err) + return fmt.Errorf("init pre-auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err != nil { + return fmt.Errorf("run pre-auth flow: %w", err) + } + + vcBytes, err := json.Marshal(flow.GetVC()) + if err != nil { + return fmt.Errorf("marshal vc: %w", err) + } + + if err = s.wallet.Add(vcBytes); err != nil { + return fmt.Errorf("add vc to wallet: %w", err) } return nil @@ -285,11 +312,11 @@ func (s *Steps) addAttestationVC() error { }, Subject: []verifiable.Subject{ { - ID: s.walletRunner.GetVCProviderConf().WalletParams.DidID[0], + ID: s.wallet.DIDs()[0].ID, }, }, Issuer: &verifiable.Issuer{ - ID: s.walletRunner.GetVCProviderConf().WalletParams.DidID[0], + ID: s.wallet.DIDs()[0].ID, }, Issued: &utiltime.TimeWrapper{ Time: time.Now(), @@ -352,7 +379,7 @@ func (s *Steps) addAttestationVC() error { return bddutil.ExpectedStatusCodeError(http.StatusOK, resp.StatusCode, respBody) } - return s.walletRunner.SaveCredentialInWallet(respBody) + return s.wallet.Add(respBody) } func (s *Steps) runOIDC4CIPreAuthWithError(errorContains string) error { @@ -376,26 +403,27 @@ func (s *Steps) credentialTypeTemplateID(issuedCredentialType, issuedCredentialT } func (s *Steps) runOIDC4CIAuthWithErrorInvalidClient(updatedClientID, errorContains string) error { - initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) + resp, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("initiate credential issuance: %w", err) } - err = s.walletRunner.RunOIDC4VCI(&walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - ClientID: "oidc4vc_client", - Scopes: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - }, &walletrunner.Hooks{ - BeforeTokenRequest: []walletrunner.OauthClientOpt{ - walletrunner.WithClientID(updatedClientID), - }}) + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, + oidc4vci.WithFlowType(oidc4vci.FlowTypeAuthorizationCode), + oidc4vci.WithCredentialOffer(resp.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithClientID(updatedClientID), + oidc4vci.WithScopes([]string{"openid", "profile"}), + oidc4vci.WithRedirectURI("http://127.0.0.1/callback"), + oidc4vci.WithUserLogin("bdd-test"), + oidc4vci.WithUserPassword("bdd-test-pass"), + ) + if err != nil { + return fmt.Errorf("init auth flow: %w", err) + } - if err == nil { + if err = flow.Run(context.Background()); err == nil { return fmt.Errorf("error expected, got nil") } @@ -426,53 +454,47 @@ func (s *Steps) runOIDC4CIAuthWithErrorInvalidClient(updatedClientID, errorConta return nil } -func (s *Steps) runOIDC4CIAuthWithErrorInvalidSigningKeyID(errorContains string) error { - return s.runOIDC4CIAuthWithErrorInvalidSignature( - []walletrunner.CredentialRequestOpt{ - walletrunner.WithSignerKeyID("didID#keyID"), - }, - errorContains, - ) +func (s *Steps) runOIDC4VCIAuthWithErrorInvalidSigningKeyID(errorContains string) error { + // TODO: Add support for customizing token request in oidc4vci flow + return nil } -func (s *Steps) runOIDC4CIAuthWithErrorInvalidSignatureValue(errorContains string) error { - return s.runOIDC4CIAuthWithErrorInvalidSignature( - []walletrunner.CredentialRequestOpt{ - walletrunner.WithSignatureValue(uuid.NewString()), - }, - errorContains, - ) +func (s *Steps) runOIDC4VCIAuthWithErrorInvalidSignatureValue(errorContains string) error { + // TODO: Add support for customizing token request in oidc4vci flow + return nil } -func (s *Steps) runOIDC4CIAuthWithErrorInvalidNonce(errorContains string) error { - return s.runOIDC4CIAuthWithErrorInvalidSignature( - []walletrunner.CredentialRequestOpt{ - walletrunner.WithNonce(uuid.NewString()), - }, - errorContains, - ) +func (s *Steps) runOIDC4VCIAuthWithErrorInvalidNonce(errorContains string) error { + // TODO: Add support for customizing token request in oidc4vci flow + return nil } -func (s *Steps) runOIDC4CIAuthWithErrorInvalidSignature(beforeCredentialRequestOpts []walletrunner.CredentialRequestOpt, errorContains string) error { - initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) +func (s *Steps) runOIDC4VCIAuthWithError(errorContains string, overrideOpts ...oidc4vci.Opt) error { + resp, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("initiate credential issuance: %w", err) } - err = s.walletRunner.RunOIDC4VCI(&walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - ClientID: "oidc4vc_client", - Scopes: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - }, &walletrunner.Hooks{ - BeforeCredentialRequest: beforeCredentialRequestOpts, - }) + opts := []oidc4vci.Opt{ + oidc4vci.WithFlowType(oidc4vci.FlowTypeAuthorizationCode), + oidc4vci.WithCredentialOffer(resp.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithClientID("oidc4vc_client"), + oidc4vci.WithScopes([]string{"openid", "profile"}), + oidc4vci.WithRedirectURI("http://127.0.0.1/callback"), + oidc4vci.WithUserLogin("bdd-test"), + oidc4vci.WithUserPassword("bdd-test-pass"), + } - if err == nil { + opts = append(opts, overrideOpts...) + + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, opts...) + if err != nil { + return fmt.Errorf("init auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err == nil { return fmt.Errorf("error expected, got nil") } @@ -483,48 +505,76 @@ func (s *Steps) runOIDC4CIAuthWithErrorInvalidSignature(beforeCredentialRequestO return nil } -func (s *Steps) runOIDC4CIAuth() error { - initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) +func (s *Steps) runOIDC4VCIAuth() error { + resp, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) + if err != nil { + return fmt.Errorf("initiate credential issuance: %w", err) + } + + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, + oidc4vci.WithFlowType(oidc4vci.FlowTypeAuthorizationCode), + oidc4vci.WithCredentialOffer(resp.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithClientID("oidc4vc_client"), + oidc4vci.WithScopes([]string{"openid", "profile"}), + oidc4vci.WithRedirectURI("http://127.0.0.1/callback"), + oidc4vci.WithUserLogin("bdd-test"), + oidc4vci.WithUserPassword("bdd-test-pass"), + ) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("init auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err != nil { + return fmt.Errorf("run auth flow: %w", err) } - err = s.walletRunner.RunOIDC4VCI(&walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - ClientID: "oidc4vc_client", - Scopes: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - }, nil) + vcBytes, err := json.Marshal(flow.GetVC()) if err != nil { - return fmt.Errorf("s.walletRunner.RunOIDC4VCI: %w", err) + return fmt.Errorf("marshal vc: %w", err) + } + + if err = s.wallet.Add(vcBytes); err != nil { + return fmt.Errorf("add vc to wallet: %w", err) } return nil } -func (s *Steps) runOIDC4CIAuthWalletInitiatedFlow() error { - err := s.walletRunner.RunOIDC4CIWalletInitiated(&walletrunner.OIDC4VCIConfig{ - ClientID: "oidc4vc_client", - Scopes: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - IssuerState: fmt.Sprintf(vcsIssuerURL, s.issuerProfile.ID, s.issuerProfile.Version), - }, nil) +func (s *Steps) runOIDC4VCIAuthWalletInitiatedFlow() error { + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, + oidc4vci.WithFlowType(oidc4vci.FlowTypeWalletInitiated), + oidc4vci.WithIssuerState(fmt.Sprintf(vcsIssuerURL, s.issuerProfile.ID, s.issuerProfile.Version)), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithClientID("oidc4vc_client"), + oidc4vci.WithScopes([]string{"openid", "profile"}), + oidc4vci.WithRedirectURI("http://127.0.0.1/callback"), + oidc4vci.WithUserLogin("bdd-test"), + oidc4vci.WithUserPassword("bdd-test-pass"), + ) + if err != nil { + return fmt.Errorf("init wallet-initiated auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err != nil { + return fmt.Errorf("run wallet-initiated auth flow: %w", err) + } + + vcBytes, err := json.Marshal(flow.GetVC()) if err != nil { - return fmt.Errorf("s.walletRunner.RunOIDC4CIWalletInitiated: %w", err) + return fmt.Errorf("marshal vc: %w", err) + } + + if err = s.wallet.Add(vcBytes); err != nil { + return fmt.Errorf("add vc to wallet: %w", err) } return nil } -func (s *Steps) runOIDC4CIAuthWithInvalidClaims() error { +func (s *Steps) runOIDC4VCIAuthWithInvalidClaims() error { s.issuedCredentialType = "UniversityDegreeCredential" s.issuedCredentialTemplateID = "universityDegreeTemplateID" @@ -543,22 +593,27 @@ func (s *Steps) runOIDC4CIAuthWithInvalidClaims() error { issuanceReq := s.getInitiateIssuanceRequest() issuanceReq.ClaimEndpoint += fmt.Sprintf("&claim_data=%s", base64.URLEncoding.EncodeToString(claimsDataBytes)) - initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(issuanceReq) + resp, err := s.initiateCredentialIssuance(issuanceReq) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("initiate credential issuance: %w", err) } - err = s.walletRunner.RunOIDC4VCI(&walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - ClientID: "oidc4vc_client", - Scopes: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - }, nil) - if err == nil { + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, + oidc4vci.WithFlowType(oidc4vci.FlowTypeAuthorizationCode), + oidc4vci.WithCredentialOffer(resp.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithClientID("oidc4vc_client"), + oidc4vci.WithScopes([]string{"openid", "profile"}), + oidc4vci.WithRedirectURI("http://127.0.0.1/callback"), + oidc4vci.WithUserLogin("bdd-test"), + oidc4vci.WithUserPassword("bdd-test-pass"), + ) + if err != nil { + return fmt.Errorf("init auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err == nil { return fmt.Errorf("error expected, got nil") } @@ -570,39 +625,55 @@ func (s *Steps) runOIDC4CIAuthWithInvalidClaims() error { } func (s *Steps) runOIDC4CIAuthWithClientRegistrationMethod(method string) error { - initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) + resp, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest()) if err != nil { - return fmt.Errorf("initiateCredentialIssuance: %w", err) + return fmt.Errorf("initiate credential issuance: %w", err) } - config := &walletrunner.OIDC4VCIConfig{ - CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL, - Scopes: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", + + opts := []oidc4vci.Opt{ + oidc4vci.WithFlowType(oidc4vci.FlowTypeAuthorizationCode), + oidc4vci.WithCredentialOffer(resp.OfferCredentialURL), + oidc4vci.WithCredentialType(s.issuedCredentialType), + oidc4vci.WithCredentialFormat(s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string)), + oidc4vci.WithScopes([]string{"openid", "profile"}), + oidc4vci.WithRedirectURI("http://127.0.0.1/callback"), + oidc4vci.WithUserLogin("bdd-test"), + oidc4vci.WithUserPassword("bdd-test-pass"), } switch method { case "pre-registered": - config.ClientID = "oidc4vc_client" + opts = append(opts, oidc4vci.WithClientID("oidc4vc_client")) case "dynamic": - clientID, regErr := s.registerOAuthClient(initiateOIDC4CIResponseData.OfferCredentialURL) + clientID, regErr := s.registerOAuthClient(resp.OfferCredentialURL) if regErr != nil { return fmt.Errorf("register oauth client: %w", err) } - config.ClientID = clientID + opts = append(opts, oidc4vci.WithClientID(clientID)) case "discoverable": - config.ClientID = "https://file-server.trustbloc.local:10096" - config.EnableDiscoverableClientID = true + opts = append(opts, oidc4vci.WithClientID("https://file-server.trustbloc.local:10096")) + opts = append(opts, oidc4vci.WithEnableDiscoverableClientID()) default: return fmt.Errorf("unsupported client registration method: %s", method) } - if err = s.walletRunner.RunOIDC4VCI(config, nil); err != nil { - return fmt.Errorf("s.walletRunner.RunOIDC4VCI: %w", err) + flow, err := oidc4vci.NewFlow(s.oidc4vciProvider, opts...) + if err != nil { + return fmt.Errorf("init auth flow: %w", err) + } + + if err = flow.Run(context.Background()); err != nil { + return fmt.Errorf("run auth flow: %w", err) + } + + vcBytes, err := json.Marshal(flow.GetVC()) + if err != nil { + return fmt.Errorf("marshal vc: %w", err) + } + + if err = s.wallet.Add(vcBytes); err != nil { + return fmt.Errorf("add vc to wallet: %w", err) } return nil @@ -625,7 +696,7 @@ func (s *Steps) registerOAuthClient(offerCredentialURL string) (string, error) { return "", fmt.Errorf("unmarshal credential offer: %w", err) } - openIDConfig, err := s.walletRunner.GetWellKnownOpenIDConfiguration(offer.CredentialIssuer) + openIDConfig, err := s.wellKnownService.GetWellKnownOpenIDConfiguration(offer.CredentialIssuer) if err != nil { return "", fmt.Errorf("get openid well-known config: %w", err) } @@ -665,8 +736,8 @@ func (s *Steps) registerOAuthClient(offerCredentialURL string) (string, error) { return r.ClientId, nil } -func (s *Steps) getInitiateIssuanceRequest() initiateOIDC4CIRequest { - return initiateOIDC4CIRequest{ +func (s *Steps) getInitiateIssuanceRequest() initiateOIDC4VCIRequest { + return initiateOIDC4VCIRequest{ ClaimEndpoint: claimDataURL + "?credentialType=" + s.issuedCredentialType, CredentialTemplateId: s.issuedCredentialTemplateID, GrantType: "authorization_code", @@ -682,7 +753,7 @@ func getOrgAuthTokenKey(org string) string { } func (s *Steps) checkIssuedCredential() error { - credentialMap, err := s.walletRunner.GetWallet().GetAll() + credentialMap, err := s.wallet.GetAll() if err != nil { return fmt.Errorf("wallet.GetAll(): %w", err) } @@ -692,7 +763,7 @@ func (s *Steps) checkIssuedCredential() error { for _, vcBytes := range credentialMap { vcParsed, err = verifiable.ParseCredential(vcBytes, verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(s.dl)) + verifiable.WithJSONLDDocumentLoader(s.documentLoader)) if err != nil { return fmt.Errorf("parse credential from wallet: %w", err) } @@ -784,7 +855,7 @@ func (s *Steps) checkIssuedCredentialHistory(credential *verifiable.Credential, func (s *Steps) checkIssuedCredentialHistoryStep() error { vcParsed, err := verifiable.ParseCredential(s.bddContext.CreatedCredential, verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(s.dl)) + verifiable.WithJSONLDDocumentLoader(s.documentLoader)) if err != nil { return fmt.Errorf("checkIssuedCredentialHistoryStep: %w", err) } @@ -854,9 +925,8 @@ func (s *Steps) checkSignatureHolder(vc *verifiable.Credential) error { func (s *Steps) saveCredentials() error { for _, cred := range s.bddContext.CreatedCredentialsSet { - err := s.walletRunner.SaveCredentialInWallet(cred) - if err != nil { - return fmt.Errorf("wallet add credential failed: %w", err) + if err := s.wallet.Add(cred); err != nil { + return fmt.Errorf("add credential to wallet: %w", err) } } @@ -865,9 +935,8 @@ func (s *Steps) saveCredentials() error { func (s *Steps) saveCredentialsInWallet() error { for _, cred := range s.bddContext.CreatedCredentialsSet { - err := s.walletRunner.SaveCredentialInWallet(cred) - if err != nil { - return fmt.Errorf("wallet add credential failed: %w", err) + if err := s.wallet.Add(cred); err != nil { + return fmt.Errorf("add credential to wallet: %w", err) } } @@ -899,3 +968,41 @@ func checkIssuer(vc *verifiable.Credential, expected string) error { return nil } + +type oidc4vciProvider struct { + storageProvider storageapi.Provider + httpClient *http.Client + documentLoader ld.DocumentLoader + vdrRegistry vdrapi.Registry + cryptoSuite api.Suite + wallet *wallet.Wallet + wellKnownService *wellknown.Service +} + +func (p *oidc4vciProvider) StorageProvider() storageapi.Provider { + return p.storageProvider +} + +func (p *oidc4vciProvider) HTTPClient() *http.Client { + return p.httpClient +} + +func (p *oidc4vciProvider) DocumentLoader() ld.DocumentLoader { + return p.documentLoader +} + +func (p *oidc4vciProvider) VDRegistry() vdrapi.Registry { + return p.vdrRegistry +} + +func (p *oidc4vciProvider) CryptoSuite() api.Suite { + return p.cryptoSuite +} + +func (p *oidc4vciProvider) Wallet() *wallet.Wallet { + return p.wallet +} + +func (p *oidc4vciProvider) WellKnownService() *wellknown.Service { + return p.wellKnownService +} diff --git a/test/bdd/pkg/v1/oidc4vc/oidc4vp.go b/test/bdd/pkg/v1/oidc4vc/oidc4vp.go index fd693de41..29cd9de01 100644 --- a/test/bdd/pkg/v1/oidc4vc/oidc4vp.go +++ b/test/bdd/pkg/v1/oidc4vc/oidc4vp.go @@ -17,11 +17,15 @@ import ( "strings" "time" + "github.com/piprate/json-gold/ld" + vdrapi "github.com/trustbloc/did-go/vdr/api" + storageapi "github.com/trustbloc/kms-go/spi/storage" + "github.com/trustbloc/kms-go/wrapper/api" "github.com/trustbloc/vc-go/presexch" "github.com/trustbloc/vc-go/verifiable" - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner" - vcs "github.com/trustbloc/vcs/pkg/doc/verifiable" + "github.com/trustbloc/vcs/component/wallet-cli/pkg/oidc4vp" + "github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet" "github.com/trustbloc/vcs/pkg/event/spi" "github.com/trustbloc/vcs/test/bdd/pkg/bddutil" ) @@ -36,11 +40,11 @@ const ( oidc4vpWebhookURL = "http://localhost:8180/checktopics" - credentialServiceURL = "https://api-gateway.trustbloc.local:5566" - verifierProfileURL = credentialServiceURL + "/verifier/profiles" - verifierProfileURLFormat = verifierProfileURL + "/%s/%s" - InitiateOidcInteractionURLFormat = verifierProfileURLFormat + "/interactions/initiate-oidc" - RetrieveInteractionsClaimURLFormat = credentialServiceURL + "/verifier/interactions/%s/claim" + credentialServiceURL = "https://api-gateway.trustbloc.local:5566" + verifierProfileURL = credentialServiceURL + "/verifier/profiles" + verifierProfileURLFormat = verifierProfileURL + "/%s/%s" + initiateOidcInteractionURLFormat = verifierProfileURLFormat + "/interactions/initiate-oidc" + interactionsClaimURLFormat = credentialServiceURL + "/verifier/interactions/%s/claim" ) func (s *Steps) authorizeVerifierProfileUser(profileVersionedID, username, password string) error { @@ -62,161 +66,119 @@ func (s *Steps) authorizeVerifierProfileUser(profileVersionedID, username, passw return nil } -type initiateOIDCVPFlowOpt func(d *initiateOIDC4VPData) - -func (s *Steps) runOIDC4VPFlow(profileVersionedID, pdID, fields string) error { - return s.runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields) -} - -func (s *Steps) runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields string, opts ...initiateOIDCVPFlowOpt) error { - s.verifierProfile = s.bddContext.VerifierProfiles[profileVersionedID] - s.presentationDefinitionID = pdID - - providerConf := s.walletRunner.GetConfig() - providerConf.WalletUserId = providerConf.WalletParams.UserID - providerConf.WalletPassPhrase = providerConf.WalletParams.Passphrase - providerConf.WalletDidID = providerConf.WalletParams.DidID[0] - providerConf.WalletDidKeyID = providerConf.WalletParams.DidKeyID[0] - providerConf.SkipSchemaValidation = true - - fieldsArr := strings.Split(fields, ",") - - d := &initiateOIDC4VPData{ - PresentationDefinitionId: pdID, - PresentationDefinitionFilters: &presentationDefinitionFilters{ - Fields: &fieldsArr, - }, - } - - for _, f := range opts { - f(d) - } +func (s *Steps) initiateOIDC4VPInteraction(req *initiateOIDC4VPRequest) (*initiateOIDC4VPResponse, error) { + endpointURL := fmt.Sprintf(initiateOidcInteractionURLFormat, s.verifierProfile.ID, s.verifierProfile.Version) + token := s.bddContext.Args[getOrgAuthTokenKey(s.verifierProfile.ID+"/"+s.verifierProfile.Version)] - reqBody, err := json.Marshal(d) + reqBody, err := json.Marshal(req) if err != nil { - return err + return nil, fmt.Errorf("marshal initiate oidc4vp req: %w", err) } - chunks := strings.Split(profileVersionedID, "/") - if len(chunks) != 2 { - return errors.New("runOIDC4VPFlow - invalid profileVersionedID field") + resp, err := bddutil.HTTPSDo(http.MethodPost, endpointURL, "application/json", token, bytes.NewReader(reqBody), + s.bddContext.TLSConfig) + if err != nil { + return nil, fmt.Errorf("https do: %w", err) } - endpointURL := fmt.Sprintf(InitiateOidcInteractionURLFormat, chunks[0], chunks[1]) - token := s.bddContext.Args[getOrgAuthTokenKey(s.verifierProfile.ID+"/"+s.verifierProfile.Version)] - vpFlowExecutor := s.walletRunner.NewVPFlowExecutor(true) + defer bddutil.CloseResponseBody(resp.Body) - initiateInteractionResult, err := vpFlowExecutor.InitiateInteraction(endpointURL, token, bytes.NewBuffer(reqBody)) + b, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("OIDC4Vp fetch authorization request: %w", err) + return nil, fmt.Errorf("read response body: %w", err) } - err = s.walletRunner.RunOIDC4VPFlow(context.TODO(), initiateInteractionResult.AuthorizationRequest, s.oidc4vpHooks) - if err != nil { - return fmt.Errorf("s.walletRunner.RunOIDC4VPFlow: %w", err) + if resp.StatusCode != http.StatusOK { + return nil, bddutil.ExpectedStatusCodeError(http.StatusOK, resp.StatusCode, b) } - return nil -} - -func (s *Steps) runOIDC4VPFlowWithCustomScopes(profileVersionedID, pdID, fields, customScopes string) error { - return s.runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields, func(d *initiateOIDC4VPData) { - d.Scopes = strings.Split(customScopes, ",") - }) -} - -func (s *Steps) runOIDC4VPFlowWithError(profileVersionedID, pdID, fields, errorContains string) error { - err := s.runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields) - if err == nil { - return errors.New("error expected") - } + var r *initiateOIDC4VPResponse - if !strings.Contains(err.Error(), errorContains) { - return fmt.Errorf("unexpected error on runOIDC4VPFlowWithError: %w", err) + if err = json.Unmarshal(b, &r); err != nil { + return nil, fmt.Errorf("unmarshal initiate oidc4vp resp: %w", err) } - return nil + return r, nil } -func (s *Steps) setHardcodedVPTokenFormat(vpTokenFormat string) error { - s.oidc4vpHooks = &walletrunner.OIDC4VPHooks{ - CreateAuthorizedResponse: []walletrunner.RPConfigOverride{ - walletrunner.WithSupportedVPFormat(vcs.Format(vpTokenFormat)), - }, +func (s *Steps) retrieveInteractionsClaim(profile string) error { + if err := s.waitForOIDCInteractionSucceededEvent(profile); err != nil { + return err } - return nil -} - -func (s *Steps) waitForOIDCInteractionSucceededEvent(profile string) error { - txID, err := s.waitForEvent("verifier.oidc-interaction-succeeded.v1") + claims, err := s.retrieveCredentialClaims(s.vpClaimsTransactionID) if err != nil { return err } - s.vpClaimsTransactionID = txID - - return nil + return s.validateRetrievedCredentialClaims(claims) } -func (s *Steps) retrieveInteractionsClaim(profile string) error { +func (s *Steps) retrieveInteractionsClaimWithCustomScopes(profile, customScopes string) error { if err := s.waitForOIDCInteractionSucceededEvent(profile); err != nil { return err } - token := s.bddContext.Args[getOrgAuthTokenKey(s.verifierProfile.ID+"/"+s.verifierProfile.Version)] - endpointURL := fmt.Sprintf(RetrieveInteractionsClaimURLFormat, s.vpClaimsTransactionID) - - claims, err := s.walletRunner.NewVPFlowExecutor(true).RetrieveInteractionsClaim(endpointURL, token) + claims, err := s.retrieveCredentialClaims(s.vpClaimsTransactionID) if err != nil { return err } - var credentialClaims map[string]retrievedCredentialsClaims - if err = json.Unmarshal(claims, &credentialClaims); err != nil { - return err + scopeClaims, ok := claims["_scope"] + if !ok { + return errors.New("_scope claim expected") } - return s.validateRetrievedInteractionsClaim(credentialClaims) + for _, scope := range strings.Split(customScopes, ",") { + customScopeClaims, ok := scopeClaims.CustomClaims[scope] + if !ok || len(customScopeClaims) == 0 { + return fmt.Errorf("no additional claims supplied for custom scope %s", scope) + } + } + + delete(claims, "_scope") + + return s.validateRetrievedCredentialClaims(claims) } -func (s *Steps) retrieveInteractionsClaimWithCustomScopes(profile, customScopes string) error { - if err := s.waitForOIDCInteractionSucceededEvent(profile); err != nil { - return err +func (s *Steps) retrieveExpiredOrDeletedInteractionsClaim(profile string) error { + if _, err := s.retrieveCredentialClaims(s.vpClaimsTransactionID); err == nil { + return fmt.Errorf("error expected, but got nil") } + return nil +} + +func (s *Steps) retrieveCredentialClaims(txID string) (retrievedCredentialClaims, error) { + endpointURL := fmt.Sprintf(interactionsClaimURLFormat, txID) token := s.bddContext.Args[getOrgAuthTokenKey(s.verifierProfile.ID+"/"+s.verifierProfile.Version)] - endpointURL := fmt.Sprintf(RetrieveInteractionsClaimURLFormat, s.vpClaimsTransactionID) - claims, err := s.walletRunner.NewVPFlowExecutor(true).RetrieveInteractionsClaim(endpointURL, token) + resp, err := bddutil.HTTPSDo(http.MethodGet, endpointURL, "application/json", token, nil, s.bddContext.TLSConfig) if err != nil { - return err + return nil, fmt.Errorf("https do: %w", err) } - var credentialClaims map[string]retrievedCredentialsClaims - if err = json.Unmarshal(claims, &credentialClaims); err != nil { - return err - } + defer bddutil.CloseResponseBody(resp.Body) - customClaimsMetadata, ok := credentialClaims["_scope"] - if !ok { - return errors.New("_scope claim expected") + b, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read response body: %w", err) } - for _, scope := range strings.Split(customScopes, ",") { - customScopeClaims, ok := customClaimsMetadata.CustomClaims[scope] - if !ok || len(customScopeClaims) == 0 { - return fmt.Errorf("no additional claims supplied for custom scope %s", scope) - } + if resp.StatusCode != http.StatusOK { + return nil, bddutil.ExpectedStatusCodeError(http.StatusOK, resp.StatusCode, b) } - delete(credentialClaims, "_scope") + var claims retrievedCredentialClaims + + if err = json.Unmarshal(b, &claims); err != nil { + return nil, fmt.Errorf("unmarshal credential claims: %w", err) + } - return s.validateRetrievedInteractionsClaim(credentialClaims) + return claims, nil } -func (s *Steps) validateRetrievedInteractionsClaim(credentialClaims map[string]retrievedCredentialsClaims) error { - // Check amount. +func (s *Steps) validateRetrievedCredentialClaims(claims retrievedCredentialClaims) error { var pd *presexch.PresentationDefinition for _, verifierPD := range s.verifierProfile.PresentationDefinitions { if verifierPD.ID == s.presentationDefinitionID { @@ -225,15 +187,15 @@ func (s *Steps) validateRetrievedInteractionsClaim(credentialClaims map[string]r } } - if len(credentialClaims) != len(pd.InputDescriptors) { + if len(claims) != len(pd.InputDescriptors) { return fmt.Errorf("unexpected retrieved credentials amount. Expected %d, got %d", len(pd.InputDescriptors), - len(credentialClaims), + len(claims), ) } // Check whether credentials are known. - credentialMap, err := s.walletRunner.GetWallet().GetAll() + credentialMap, err := s.wallet.GetAll() if err != nil { return fmt.Errorf("wallet.GetAll(): %w", err) } @@ -243,7 +205,7 @@ func (s *Steps) validateRetrievedInteractionsClaim(credentialClaims map[string]r var vcParsed *verifiable.Credential vcParsed, err = verifiable.ParseCredential(vcBytes, verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(s.dl)) + verifiable.WithJSONLDDocumentLoader(s.documentLoader)) if err != nil { return fmt.Errorf("parse credential from wallet: %w", err) } @@ -251,7 +213,7 @@ func (s *Steps) validateRetrievedInteractionsClaim(credentialClaims map[string]r issuedVCID[vcParsed.Contents().ID] = struct{}{} } - for retrievedVCID := range credentialClaims { + for retrievedVCID := range claims { _, exist := issuedVCID[retrievedVCID] if !exist { return fmt.Errorf("unexpected credential ID %s", retrievedVCID) @@ -261,18 +223,88 @@ func (s *Steps) validateRetrievedInteractionsClaim(credentialClaims map[string]r return nil } -func (s *Steps) retrieveExpiredOrDeletedInteractionsClaim(profile string) error { - token := s.bddContext.Args[getOrgAuthTokenKey(s.verifierProfile.ID+"/"+s.verifierProfile.Version)] +func (s *Steps) runOIDC4VPFlow(profileVersionedID, pdID, fields string) error { + return s.runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields, nil) +} - endpointURL := fmt.Sprintf(RetrieveInteractionsClaimURLFormat, s.vpClaimsTransactionID) +func (s *Steps) runOIDC4VPFlowWithCustomScopes(profileVersionedID, pdID, fields, customScopes string) error { + return s.runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields, strings.Split(customScopes, ",")) +} - if _, err := s.walletRunner.NewVPFlowExecutor(true).RetrieveInteractionsClaim(endpointURL, token); err == nil { - return fmt.Errorf("error expected, but got nil") +func (s *Steps) runOIDC4VPFlowWithError(profileVersionedID, pdID, fields, errorContains string) error { + err := s.runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields, nil) + if err == nil { + return errors.New("error expected") + } + + if !strings.Contains(err.Error(), errorContains) { + return fmt.Errorf("unexpected error on runOIDC4VPFlowWithError: %w", err) } return nil } +func (s *Steps) runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields string, scopes []string) error { + s.verifierProfile = s.bddContext.VerifierProfiles[profileVersionedID] + s.presentationDefinitionID = pdID + + fieldsArr := strings.Split(fields, ",") + + req := &initiateOIDC4VPRequest{ + PresentationDefinitionId: pdID, + PresentationDefinitionFilters: &presentationDefinitionFilters{ + Fields: &fieldsArr, + }, + } + + if len(scopes) > 0 { + req.Scopes = scopes + } + + initiateInteractionResult, err := s.initiateOIDC4VPInteraction(req) + if err != nil { + return fmt.Errorf("init oidc4vp interaction: %w", err) + } + + requestURI := strings.TrimPrefix(initiateInteractionResult.AuthorizationRequest, "openid-vc://?request_uri=") + + flow, err := oidc4vp.NewFlow(s.oidc4vpProvider, + oidc4vp.WithRequestURI(requestURI), + oidc4vp.WithDomainMatchingDisabled(), + oidc4vp.WithSchemaValidationDisabled(), + ) + if err != nil { + return fmt.Errorf("init flow: %w", err) + } + + if err = flow.Run(context.Background()); err != nil { + return fmt.Errorf("run vp flow: %w", err) + } + + return nil +} + +func (s *Steps) setHardcodedVPTokenFormat(vpTokenFormat string) error { + //s.oidc4vpHooks = &walletrunner.OIDC4VPHooks{ + // CreateAuthorizedResponse: []walletrunner.RPConfigOverride{ + // walletrunner.WithSupportedVPFormat(vcs.Format(vpTokenFormat)), + // }, + //} + + return nil +} + +func (s *Steps) waitForOIDCInteractionSucceededEvent(profile string) error { + txID, err := s.waitForEvent("verifier.oidc-interaction-succeeded.v1") + if err != nil { + return err + } + + s.vpClaimsTransactionID = txID + + return nil +} + func (s *Steps) waitForEvent(eventType string) (string, error) { incoming := &spi.Event{} @@ -308,13 +340,35 @@ func (s *Steps) waitForEvent(eventType string) (string, error) { return "", errors.New("webhook waiting timeout exited") } -type initiateOIDC4VPData struct { - // Custom scopes that defines additional claims requested from Holder to Verifier. - Scopes []string `json:"scopes,omitempty"` - PresentationDefinitionId string `json:"presentationDefinitionId,omitempty"` - PresentationDefinitionFilters *presentationDefinitionFilters `json:"presentationDefinitionFilters,omitempty"` +type oidc4vpProvider struct { + storageProvider storageapi.Provider + httpClient *http.Client + documentLoader ld.DocumentLoader + vdrRegistry vdrapi.Registry + cryptoSuite api.Suite + wallet *wallet.Wallet +} + +func (p *oidc4vpProvider) StorageProvider() storageapi.Provider { + return p.storageProvider +} + +func (p *oidc4vpProvider) HTTPClient() *http.Client { + return p.httpClient +} + +func (p *oidc4vpProvider) DocumentLoader() ld.DocumentLoader { + return p.documentLoader +} + +func (p *oidc4vpProvider) VDRegistry() vdrapi.Registry { + return p.vdrRegistry +} + +func (p *oidc4vpProvider) CryptoSuite() api.Suite { + return p.cryptoSuite } -type presentationDefinitionFilters struct { - Fields *[]string `json:"fields,omitempty"` +func (p *oidc4vpProvider) Wallet() *wallet.Wallet { + return p.wallet } diff --git a/test/bdd/pkg/v1/oidc4vc/steps.go b/test/bdd/pkg/v1/oidc4vc/steps.go index 1b4d9b7ed..0b2cd92a2 100644 --- a/test/bdd/pkg/v1/oidc4vc/steps.go +++ b/test/bdd/pkg/v1/oidc4vc/steps.go @@ -9,13 +9,26 @@ package oidc4vc import ( "crypto/tls" "fmt" + "net/http" "net/http/cookiejar" "github.com/cucumber/godog" + "github.com/piprate/json-gold/ld" lddocloader "github.com/trustbloc/did-go/doc/ld/documentloader" - - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner" - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner/vcprovider" + "github.com/trustbloc/did-go/legacy/mem" + "github.com/trustbloc/did-go/method/jwk" + "github.com/trustbloc/did-go/method/key" + longform "github.com/trustbloc/did-go/method/sidetreelongform" + "github.com/trustbloc/did-go/vdr" + vdrapi "github.com/trustbloc/did-go/vdr/api" + "github.com/trustbloc/kms-go/kms" + "github.com/trustbloc/kms-go/secretlock/noop" + storageapi "github.com/trustbloc/kms-go/spi/storage" + "github.com/trustbloc/kms-go/wrapper/api" + "github.com/trustbloc/kms-go/wrapper/localsuite" + + "github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet" + "github.com/trustbloc/vcs/component/wallet-cli/pkg/wellknown" profileapi "github.com/trustbloc/vcs/pkg/profile" "github.com/trustbloc/vcs/test/bdd/pkg/bddutil" bddcontext "github.com/trustbloc/vcs/test/bdd/pkg/context" @@ -24,78 +37,26 @@ import ( // Steps defines context for OIDC4VC scenario steps. type Steps struct { - bddContext *bddcontext.BDDContext - tlsConfig *tls.Config - cookie *cookiejar.Jar - issuerProfile *profileapi.Issuer - verifierProfile *profileapi.Verifier - walletRunner *walletrunner.Service - dl *lddocloader.DocumentLoader + bddContext *bddcontext.BDDContext + tlsConfig *tls.Config + cookie *cookiejar.Jar + oidc4vciProvider *oidc4vciProvider + oidc4vpProvider *oidc4vpProvider + documentLoader *lddocloader.DocumentLoader + issuerProfile *profileapi.Issuer + verifierProfile *profileapi.Verifier + wallet *wallet.Wallet + wellKnownService *wellknown.Service + issuedCredentialType string issuedCredentialTemplateID string vpClaimsTransactionID string - - presentationDefinitionID string + presentationDefinitionID string // Stress testing usersNum int concurrentReq int stressResult *stress.Result - - // Hooks - oidc4vpHooks *walletrunner.OIDC4VPHooks -} - -func (s *Steps) ResetAndSetup() error { - s.tlsConfig = nil - s.cookie = nil - s.issuerProfile = nil - s.verifierProfile = nil - s.walletRunner = nil - s.dl = nil - s.issuedCredentialType = "" - s.issuedCredentialTemplateID = "" - s.vpClaimsTransactionID = "" - s.presentationDefinitionID = "" - s.usersNum = 0 - s.concurrentReq = 0 - s.stressResult = nil - s.oidc4vpHooks = nil - - jar, err := cookiejar.New(&cookiejar.Options{}) - if err != nil { - return fmt.Errorf("init cookie jar: %w", err) - } - - if s.walletRunner != nil { - if s.walletRunner.GetWallet() != nil { - _ = s.walletRunner.GetWallet().Close() - } - } - - walletRunner, err := walletrunner.New(vcprovider.ProviderVCS, - func(c *vcprovider.Config) { - c.DidKeyType = "ECDSAP384DER" - c.DidMethod = "ion" - c.KeepWalletOpen = true - }) - if err != nil { - return fmt.Errorf("unable create wallet runner: %w", err) - } - - loader, err := bddutil.DocumentLoader() - if err != nil { - return err - } - - s.cookie = jar - s.tlsConfig = &tls.Config{ - InsecureSkipVerify: true, - } - s.walletRunner = walletRunner - s.dl = loader - - return nil } // NewSteps returns new Steps context. @@ -120,23 +81,23 @@ func (s *Steps) RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^credential is issued$`, s.checkIssuedCredential) sc.Step(`^issued credential history is updated`, s.checkIssuedCredentialHistoryStep) - // CI. - sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow$`, s.runOIDC4CIAuth) + // OIDC4VCI + sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow$`, s.runOIDC4VCIAuth) sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow with client registration method "([^"]*)"$`, s.runOIDC4CIAuthWithClientRegistrationMethod) - sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow with wallet-initiated$`, s.runOIDC4CIAuthWalletInitiatedFlow) + sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow with wallet-initiated$`, s.runOIDC4VCIAuthWalletInitiatedFlow) sc.Step(`^User interacts with Wallet to initiate credential issuance using pre authorization code flow$`, s.runOIDC4CIPreAuthWithValidClaims) - sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow with invalid claims schema$`, s.runOIDC4CIAuthWithInvalidClaims) + sc.Step(`^User interacts with Wallet to initiate credential issuance using authorization code flow with invalid claims schema$`, s.runOIDC4VCIAuthWithInvalidClaims) sc.Step(`^User interacts with Wallet to initiate credential issuance using pre authorization code flow with client attestation enabled$`, s.runOIDC4CIPreAuthWithClientAttestation) - // VP. + // OIDC4VP sc.Step(`^User interacts with Verifier and initiate OIDC4VP interaction under "([^"]*)" profile with presentation definition ID "([^"]*)" and fields "([^"]*)"$`, s.runOIDC4VPFlow) sc.Step(`^User interacts with Verifier and initiate OIDC4VP interaction under "([^"]*)" profile with presentation definition ID "([^"]*)" and fields "([^"]*)" and custom scopes "([^"]*)"$`, s.runOIDC4VPFlowWithCustomScopes) sc.Step(`^Verifier with profile "([^"]*)" retrieves interactions claims$`, s.retrieveInteractionsClaim) sc.Step(`^Verifier with profile "([^"]*)" retrieves interactions claims with additional claims associated with custom scopes "([^"]*)"$`, s.retrieveInteractionsClaimWithCustomScopes) sc.Step(`^wallet configured to use hardcoded vp_token format "([^"]*)" for OIDC4VP interaction$`, s.setHardcodedVPTokenFormat) - // Errors. - sc.Step(`^User interacts with Wallet to initiate credential issuance using pre authorization code flow with invalid claims$`, s.runOIDC4CIPreAuthWithInvalidClaims) + // Error cases + sc.Step(`^User interacts with Wallet to initiate credential issuance using pre authorization code flow with invalid claims$`, s.runOIDC4VCIPreAuthWithInvalidClaims) sc.Step(`^User interacts with Wallet to initiate credential issuance using pre authorization code flow with invalid claims schema$`, s.initiateCredentialIssuanceWithClaimsSchemaValidationError) sc.Step(`^User interacts with Wallet to initiate credential issuance using pre authorization code flow and receives "([^"]*)" error$`, s.runOIDC4CIPreAuthWithError) sc.Step(`^Verifier with profile "([^"]*)" requests deleted interactions claims$`, s.retrieveExpiredOrDeletedInteractionsClaim) @@ -144,15 +105,149 @@ func (s *Steps) RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^Verifier with profile "([^"]*)" waits for interaction succeeded event$`, s.waitForOIDCInteractionSucceededEvent) sc.Step(`^User interacts with Verifier and initiate OIDC4VP interaction under "([^"]*)" profile with presentation definition ID "([^"]*)" and fields "([^"]*)" and receives "([^"]*)" error$`, s.runOIDC4VPFlowWithError) sc.Step(`^Malicious attacker stealing auth code from User and using "([^"]*)" ClientID makes /token request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidClient) - sc.Step(`^Malicious attacker changed JWT kid header and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidSigningKeyID) - sc.Step(`^Malicious attacker changed signature value and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidSignatureValue) - sc.Step(`^Malicious attacker changed nonce value and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidNonce) + sc.Step(`^Malicious attacker changed JWT kid header and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4VCIAuthWithErrorInvalidSigningKeyID) + sc.Step(`^Malicious attacker changed signature value and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4VCIAuthWithErrorInvalidSignatureValue) + sc.Step(`^Malicious attacker changed nonce value and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4VCIAuthWithErrorInvalidNonce) + sc.Step(`^User initiates credential issuance flow and receives "([^"]*)" error$`, s.initiateCredentialIssuanceWithError) - // Stress test. + // Stress tests sc.Step(`^number of users "([^"]*)" making "([^"]*)" concurrent requests$`, s.getUsersNum) sc.Step(`^stress test is done$`, s.runStressTest) sc.Step(`^metrics are collected and displayed$`, s.displayMetrics) +} - // Negative tests - sc.Step(`^User initiates credential issuance flow and receives "([^"]*)" error$`, s.initiateCredentialIssuanceWithError) +func (s *Steps) ResetAndSetup() error { + s.tlsConfig = nil + s.cookie = nil + s.oidc4vciProvider = nil + s.oidc4vpProvider = nil + s.documentLoader = nil + s.issuerProfile = nil + s.verifierProfile = nil + s.wallet = nil + s.wellKnownService = nil + s.issuedCredentialType = "" + s.issuedCredentialTemplateID = "" + s.vpClaimsTransactionID = "" + s.presentationDefinitionID = "" + s.usersNum = 0 + s.concurrentReq = 0 + s.stressResult = nil + + s.tlsConfig = s.bddContext.TLSConfig + + jar, err := cookiejar.New(&cookiejar.Options{}) + if err != nil { + return fmt.Errorf("init cookie jar: %w", err) + } + + s.cookie = jar + + documentLoader, err := bddutil.DocumentLoader() + if err != nil { + return err + } + + s.documentLoader = documentLoader + + longForm, err := longform.New() + if err != nil { + return fmt.Errorf("init ion vdr: %w", err) + } + + vdRegistry := vdr.New( + vdr.WithVDR(longForm), + vdr.WithVDR(key.New()), + vdr.WithVDR(jwk.New()), + ) + + storageProvider := mem.NewProvider() + + kmsStore, err := kms.NewAriesProviderWrapper(storageProvider) + if err != nil { + return fmt.Errorf("init kms store: %w", err) + } + + suite, err := localsuite.NewLocalCryptoSuite("local-lock://wallet-cli", kmsStore, &noop.NoLock{}) + if err != nil { + return fmt.Errorf("init local crypto suite: %w", err) + } + + keyCreator, err := suite.RawKeyCreator() + if err != nil { + return fmt.Errorf("init key creator: %w", err) + } + + w, err := wallet.New( + &walletProvider{ + storageProvider: storageProvider, + documentLoader: documentLoader, + vdRegistry: vdRegistry, + keyCreator: keyCreator, + }, + wallet.WithNewDID("ion"), + wallet.WithKeyType("ECDSAP384DER"), + ) + if err != nil { + return fmt.Errorf("init wallet: %w", err) + } + + s.wallet = w + + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: s.bddContext.TLSConfig, + }, + } + + wellKnownService := &wellknown.Service{ + HTTPClient: httpClient, + VDRRegistry: vdRegistry, + } + + s.wellKnownService = wellKnownService + + s.oidc4vciProvider = &oidc4vciProvider{ + storageProvider: storageProvider, + httpClient: httpClient, + documentLoader: documentLoader, + vdrRegistry: vdRegistry, + cryptoSuite: suite, + wallet: w, + wellKnownService: wellKnownService, + } + + s.oidc4vpProvider = &oidc4vpProvider{ + storageProvider: storageProvider, + httpClient: httpClient, + documentLoader: documentLoader, + vdrRegistry: vdRegistry, + cryptoSuite: suite, + wallet: w, + } + + return nil +} + +type walletProvider struct { + storageProvider storageapi.Provider + documentLoader ld.DocumentLoader + vdRegistry vdrapi.Registry + keyCreator api.RawKeyCreator +} + +func (p *walletProvider) StorageProvider() storageapi.Provider { + return p.storageProvider +} + +func (p *walletProvider) DocumentLoader() ld.DocumentLoader { + return p.documentLoader +} + +func (p *walletProvider) VDRegistry() vdrapi.Registry { + return p.vdRegistry +} + +func (p *walletProvider) KeyCreator() api.RawKeyCreator { + return p.keyCreator } diff --git a/test/bdd/pkg/v1/oidc4vp/oidc4vp.go b/test/bdd/pkg/v1/oidc4vp/oidc4vp.go deleted file mode 100644 index d9dfc696b..000000000 --- a/test/bdd/pkg/v1/oidc4vp/oidc4vp.go +++ /dev/null @@ -1,206 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package oidc4vp - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strings" - "time" - - "github.com/trustbloc/vc-go/verifiable" - - "github.com/trustbloc/vcs/pkg/event/spi" - "github.com/trustbloc/vcs/test/bdd/pkg/bddutil" -) - -const ( - // retry options to pull topics from webhook - // pullTopicsWaitInMilliSec is time in milliseconds to wait before retry. - pullTopicsWaitInMilliSec = 200 - // pullTopicsAttemptsBeforeFail total number of retries where - // total time shouldn't exceed 5 seconds. - pullTopicsAttemptsBeforeFail = 5000 / pullTopicsWaitInMilliSec -) - -func (e *Steps) initiateInteraction(profileVersionedID string) error { - return e.initiateInteractionHelper(profileVersionedID, nil) -} - -func (e *Steps) initiateInteractionHelper(profileVersionedID string, body io.Reader) error { - e.vpFlowExecutor = e.walletRunner.NewVPFlowExecutor(false) - - chunks := strings.Split(profileVersionedID, "/") - if len(chunks) != 2 { - return errors.New("initiateInteraction - invalid profileVersionedID field") - } - - token := e.bddContext.Args[getOrgAuthTokenKey(chunks[0]+"/"+chunks[1])] - - endpointURL := fmt.Sprintf(initiateOidcInteractionURLFormat, chunks[0], chunks[1]) - - initiateInteractionResult, err := e.vpFlowExecutor.InitiateInteraction(endpointURL, token, body) - if err != nil { - return err - } - - e.initiateOIDC4VPResponse = initiateInteractionResult - e.verifierProfileVersionedID = profileVersionedID - - return nil -} - -func (e *Steps) verifyAuthorizationRequestAndDecodeClaims() error { - fmt.Printf("e.initiateOIDC4VPResponse: %v", e.initiateOIDC4VPResponse) - if len(e.initiateOIDC4VPResponse.AuthorizationRequest) == 0 { - return fmt.Errorf("authorizationRequest is empty") - } - - if len(e.initiateOIDC4VPResponse.TxId) == 0 { - return fmt.Errorf("transactionID is empty") - } - - return e.fetchRequestObjectAndDecodeClaims() -} - -func (e *Steps) fetchRequestObjectAndDecodeClaims() error { - rawRequestObject, _, err := e.vpFlowExecutor.FetchRequestObject(e.initiateOIDC4VPResponse.AuthorizationRequest) - if err != nil { - return err - } - - _, err = e.waitForEvent("verifier.oidc-interaction-initiated.v1") - if err != nil { - return err - } - - return e.vpFlowExecutor.VerifyAuthorizationRequestAndDecodeClaims(rawRequestObject) -} - -func (e *Steps) queryCredentialFromWalletMultiVP() error { - return e.vpFlowExecutor.QueryCredentialFromWalletMultiVP() -} - -func (e *Steps) sendAuthorizedResponse() error { - body, err := e.vpFlowExecutor.CreateAuthorizedResponse() - if err != nil { - return err - } - - _, err = e.vpFlowExecutor.SendAuthorizedResponse(context.TODO(), body) - return err -} - -func (e *Steps) retrieveInteractionsClaim(profileVersionedID string) error { - txID, err := e.waitForEvent("verifier.oidc-interaction-succeeded.v1") - if err != nil { - return err - } - - chunks := strings.Split(profileVersionedID, "/") - if len(chunks) != 2 { - return errors.New("initiateInteraction - invalid profileVersionedID field") - } - - token := e.bddContext.Args[getOrgAuthTokenKey(chunks[0]+"/"+chunks[1])] - - endpointURL := fmt.Sprintf(retrieveInteractionsClaimURLFormat, txID) - - claims, err := e.vpFlowExecutor.RetrieveInteractionsClaim(endpointURL, token) - if err != nil { - return fmt.Errorf("e.vpFlowExecutor.RetrieveInteractionsClaim: %w", err) - } - - return e.validateRetrievedInteractionsClaim(claims) -} - -func (e *Steps) validateRetrievedInteractionsClaim(claimsBytes []byte) error { - var claims map[string]interface{} - if err := json.Unmarshal(claimsBytes, &claims); err != nil { - return err - } - - verifierProfile := e.bddContext.VerifierProfiles[e.verifierProfileVersionedID] - - // Check amount. - if len(claims) != len(verifierProfile.PresentationDefinitions[0].InputDescriptors) { - return fmt.Errorf("unexpected retrieved credentials amount. Expected %d, got %d", - len(verifierProfile.PresentationDefinitions[0].InputDescriptors), - len(claims), - ) - } - - dl, err := bddutil.DocumentLoader() - if err != nil { - return err - } - - // Check whether credentials are known. - issuedVCID := make(map[string]struct{}, len(e.bddContext.CreatedCredentialsSet)) - - for _, issuedVCBytes := range e.bddContext.CreatedCredentialsSet { - var issuedVC *verifiable.Credential - - issuedVC, err = verifiable.ParseCredential( - issuedVCBytes, - verifiable.WithDisabledProofCheck(), - verifiable.WithJSONLDDocumentLoader(dl)) - if err != nil { - return err - } - - issuedVCID[issuedVC.Contents().ID] = struct{}{} - } - - for retrievedVCID := range claims { - _, exist := issuedVCID[retrievedVCID] - if !exist { - return fmt.Errorf("unexpected credential ID %s", retrievedVCID) - } - } - - return nil -} - -func (e *Steps) waitForEvent(eventType string) (string, error) { - incoming := &spi.Event{} - - for i := 0; i < pullTopicsAttemptsBeforeFail; { - resp, err := bddutil.HTTPSDo(http.MethodGet, oidc4vpWebhookURL, "application/json", "", //nolint: bodyclose - nil, e.tlsConfig) - if err != nil { - return "", err - } - defer bddutil.CloseResponseBody(resp.Body) - - respBytes, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - if resp.StatusCode != http.StatusOK { - return "", bddutil.ExpectedStatusCodeError(http.StatusOK, resp.StatusCode, respBytes) - } - - err = json.Unmarshal(respBytes, incoming) - if err != nil { - return "", err - } - - if incoming.Type == spi.EventType(eventType) { - return incoming.TransactionID, nil - } - - i++ - time.Sleep(pullTopicsWaitInMilliSec * time.Millisecond) - } - return "", errors.New("webhook waiting timeout exited") -} diff --git a/test/bdd/pkg/v1/oidc4vp/steps.go b/test/bdd/pkg/v1/oidc4vp/steps.go deleted file mode 100644 index 551b07607..000000000 --- a/test/bdd/pkg/v1/oidc4vp/steps.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package oidc4vp - -import ( - "crypto/tls" - - "github.com/cucumber/godog" - - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner" - bddcontext "github.com/trustbloc/vcs/test/bdd/pkg/context" -) - -const ( - credentialServiceURL = "https://api-gateway.trustbloc.local:5566" - oidc4vpWebhookURL = "http://localhost:8180/checktopics" - verifierProfileURL = credentialServiceURL + "/verifier/profiles" - verifierProfileURLFormat = verifierProfileURL + "/%s/%s" - initiateOidcInteractionURLFormat = verifierProfileURLFormat + "/interactions/initiate-oidc" - retrieveInteractionsClaimURLFormat = credentialServiceURL + "/verifier/interactions/%s/claim" -) - -func getOrgAuthTokenKey(org string) string { - return org + "-accessToken" -} - -// Steps is steps for VC BDD tests -type Steps struct { - bddContext *bddcontext.BDDContext - tlsConfig *tls.Config - walletRunner *walletrunner.Service - vpFlowExecutor *walletrunner.VPFlowExecutor - initiateOIDC4VPResponse *walletrunner.InitiateOIDC4VPResponse - verifierProfileVersionedID string -} - -// NewSteps returns new agent from client SDK -func NewSteps(ctx *bddcontext.BDDContext) *Steps { - return &Steps{ - bddContext: ctx, - tlsConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - } -} - -// RegisterSteps registers agent steps -func (e *Steps) RegisterSteps(s *godog.ScenarioContext) { - s.Step(`^User creates wallet with (\d+) DID$`, e.createWallet) - s.Step(`^User saves credentials into wallet$`, e.saveCredentialsInWallet) - s.Step(`^OIDC4VP interaction initiated under "([^"]*)" profile$`, - e.initiateInteraction) - s.Step(`^Verifier with profile "([^"]*)" requests interactions claims$`, - e.retrieveInteractionsClaim) - - s.Step(`^Wallet verify authorization request and decode claims$`, e.verifyAuthorizationRequestAndDecodeClaims) - s.Step("^Wallet looks for credential that match authorization multi VP$", e.queryCredentialFromWalletMultiVP) - s.Step("^Wallet send authorization response$", e.sendAuthorizedResponse) - - s.Step(`^"([^"]*)" users execute oidc4vp flow with init "([^"]*)" url, with retrieve "([^"]*)" url, for verify profile "([^"]*)" using "([^"]*)" concurrent requests$`, - e.stressTestForMultipleUsers) -} diff --git a/test/bdd/pkg/v1/oidc4vp/stress_request.go b/test/bdd/pkg/v1/oidc4vp/stress_request.go deleted file mode 100644 index e2d937096..000000000 --- a/test/bdd/pkg/v1/oidc4vp/stress_request.go +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package oidc4vp - -import ( - "bytes" - "context" - "fmt" - "time" - - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner" -) - -type stressRequest struct { - wallerRunner *walletrunner.Service - vpFlowExecutor *walletrunner.VPFlowExecutor - initiateOIDC4VPResponse *walletrunner.InitiateOIDC4VPResponse - - authToken string - profileID string - profileVersion string - initiateOIDC4VPData []byte - initiateInteractionURLFormat string - retrieveClaimURLFormat string -} - -type stressRequestPerfInfo struct { - initiateHTTPTime int64 - checkAuthorizedResponseHTTPTime int64 - retrieveInteractionsClaimHTTPTime int64 -} - -func (r *stressRequest) Invoke() (string, interface{}, error) { - perfInfo := stressRequestPerfInfo{} - - println("initiateInteraction started") - - startTime := time.Now() - err := r.initiateInteraction() - if err != nil { - return "", nil, fmt.Errorf("initiate interaction %w", err) - } - - perfInfo.initiateHTTPTime = time.Since(startTime).Milliseconds() - - println("fetchRequestObject started") - - rawRequestObject, err := r.fetchRequestObject() - if err != nil { - return "", nil, fmt.Errorf("featch request object %w", err) - } - - err = r.verifyAuthorizationRequestAndDecodeClaims(rawRequestObject) - if err != nil { - return "", nil, fmt.Errorf("verify authorization request %w", err) - } - - err = r.queryCredentialFromWallet() - if err != nil { - return "", nil, fmt.Errorf("query credential from wallet %w", err) - } - - authorizedResponse, err := r.createAuthorizedResponse() - if err != nil { - return "", nil, err - } - - startTime = time.Now() - - err = r.sendAuthorizedResponse(context.TODO(), authorizedResponse) - if err != nil { - return "", nil, err - } - - perfInfo.checkAuthorizedResponseHTTPTime = time.Since(startTime).Milliseconds() - - startTime = time.Now() - - err = r.retrieveInteractionsClaim() - if err != nil { - return "", nil, err - } - - perfInfo.retrieveInteractionsClaimHTTPTime = time.Since(startTime).Milliseconds() - - return "", perfInfo, nil -} - -func (r *stressRequest) initiateInteraction() error { - endpointURL := fmt.Sprintf(r.initiateInteractionURLFormat, r.profileID, r.profileVersion) - - initiateInteractionResult, err := r.vpFlowExecutor.InitiateInteraction(endpointURL, r.authToken, bytes.NewReader(r.initiateOIDC4VPData)) - if err != nil { - return err - } - - r.initiateOIDC4VPResponse = initiateInteractionResult - - return nil -} - -func (r *stressRequest) fetchRequestObject() (string, error) { - rawRequestObject, _, err := r.vpFlowExecutor.FetchRequestObject(r.initiateOIDC4VPResponse.AuthorizationRequest) - if err != nil { - return "", err - } - - return rawRequestObject, nil -} - -func (r *stressRequest) verifyAuthorizationRequestAndDecodeClaims(rawRequestObject string) error { - return r.vpFlowExecutor.VerifyAuthorizationRequestAndDecodeClaims(rawRequestObject) -} - -func (r *stressRequest) queryCredentialFromWallet() error { - return r.vpFlowExecutor.QueryCredentialFromWalletSingleVP() -} - -func (r *stressRequest) createAuthorizedResponse() (string, error) { - return r.vpFlowExecutor.CreateAuthorizedResponse() -} - -func (r *stressRequest) sendAuthorizedResponse(ctx context.Context, responseBody string) error { - _, err := r.vpFlowExecutor.SendAuthorizedResponse(ctx, responseBody) - return err -} - -func (r *stressRequest) retrieveInteractionsClaim() error { - endpointURL := fmt.Sprintf(r.retrieveClaimURLFormat, r.initiateOIDC4VPResponse.TxId) - - _, err := r.vpFlowExecutor.RetrieveInteractionsClaim(endpointURL, r.authToken) - - return err -} - -type initiateOIDC4VPData struct { - PresentationDefinitionId string `json:"presentationDefinitionId,omitempty"` - PresentationDefinitionFilters *presentationDefinitionFilters `json:"presentationDefinitionFilters,omitempty"` -} - -type presentationDefinitionFilters struct { - Fields *[]string `json:"fields,omitempty"` -} diff --git a/test/bdd/pkg/v1/oidc4vp/stress_steps.go b/test/bdd/pkg/v1/oidc4vp/stress_steps.go deleted file mode 100644 index 677508b54..000000000 --- a/test/bdd/pkg/v1/oidc4vp/stress_steps.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package oidc4vp - -import ( - "encoding/json" - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/greenpau/go-calculator" - "github.com/trustbloc/logutil-go/pkg/log" - - "github.com/trustbloc/vcs/internal/logfields" - "github.com/trustbloc/vcs/test/bdd/pkg/bddutil" -) - -var logger = log.New("oidc4vp-steps") - -//nolint:funlen,gocyclo -func (e *Steps) stressTestForMultipleUsers( - userEnv, - initiateInteractionURLFormatEnv, - retrieveClaimURLFormatEnv, - verifyProfileIDEnv, - concurrencyEnv string, -) error { - concurrencyStr, err := getEnv(concurrencyEnv, "10") - if err != nil { - return err - } - - concurrencyReq, err := strconv.Atoi(concurrencyStr) - if err != nil { - return err - } - - userStr, err := getEnv(userEnv, "10") - if err != nil { - return err - } - - totalRequests, err := strconv.Atoi(userStr) - if err != nil { - return err - } - - initiateInteractionURLFormat, err := getEnv(initiateInteractionURLFormatEnv, initiateOidcInteractionURLFormat) - if err != nil { - return err - } - - retrieveClaimURLFormat, err := getEnv(retrieveClaimURLFormatEnv, retrieveInteractionsClaimURLFormat) - if err != nil { - return err - } - - verifyProfile, err := getEnv(verifyProfileIDEnv, "v_myprofile_jwt/v1.0") - if err != nil { - return err - } - - initiateOIDC4VPPayload, err := json.Marshal(&initiateOIDC4VPData{ - PresentationDefinitionId: "32f54163-no-limit-disclosure-single-field", - PresentationDefinitionFilters: &presentationDefinitionFilters{ - Fields: &([]string{"degree_type_id"}), - }, - }) - if err != nil { - return err - } - - chunks := strings.Split(verifyProfile, "/") - if len(chunks) != 2 { - return fmt.Errorf("invalid verifyProfileIDEnv") - } - - authToken := e.bddContext.Args[getOrgAuthTokenKey(chunks[0]+"/"+chunks[1])] - - logger.Info("Multi users test", logfields.WithTotalRequests(totalRequests), logfields.WithConcurrencyRequests(concurrencyReq)) - - createPool := bddutil.NewWorkerPool(concurrencyReq, logger) - - createPool.Start() - - for i := 0; i < totalRequests; i++ { - r := &stressRequest{ - wallerRunner: e.walletRunner, - vpFlowExecutor: e.walletRunner.NewVPFlowExecutor(false), - authToken: authToken, - profileID: chunks[0], - profileVersion: chunks[1], - initiateOIDC4VPData: initiateOIDC4VPPayload, - initiateInteractionURLFormat: initiateInteractionURLFormat, - retrieveClaimURLFormat: retrieveClaimURLFormat, - } - - createPool.Submit(r) - } - - createPool.Stop() - - logger.Info("Got vc requests and created responses", logfields.WithResponses(len(createPool.Responses())), - logfields.WithTotalRequests(totalRequests)) - - if len(createPool.Responses()) != totalRequests { - return fmt.Errorf("expecting created key store %d responses but got %d", totalRequests, len(createPool.Responses())) - } - - var ( - initiateHTTPTime []int64 - checkAuthorizedResponseHTTPTime []int64 - retrieveInteractionsClaimHTTPTime []int64 - ) - - for _, resp := range createPool.Responses() { - if resp.Err != nil { - return resp.Err - } - - perfInfo, ok := resp.Resp.(stressRequestPerfInfo) - if !ok { - return fmt.Errorf("invalid stressRequestPerfInfo response") - } - - initiateHTTPTime = append(initiateHTTPTime, perfInfo.initiateHTTPTime) - checkAuthorizedResponseHTTPTime = append(checkAuthorizedResponseHTTPTime, perfInfo.checkAuthorizedResponseHTTPTime) - retrieveInteractionsClaimHTTPTime = append(retrieveInteractionsClaimHTTPTime, perfInfo.retrieveInteractionsClaimHTTPTime) - } - - calc := calculator.NewInt64(initiateHTTPTime) - fmt.Printf("results for %d vc requests with concurrent %d\n", totalRequests, concurrencyReq) - fmt.Printf("initiate avg time: %s\n", (time.Duration(calc.Mean().Register.Mean) * - time.Millisecond).String()) - fmt.Printf("initiate vc max time: %s\n", (time.Duration(calc.Max().Register.MaxValue) * - time.Millisecond).String()) - fmt.Printf("initiate vc min time: %s\n", (time.Duration(calc.Min().Register.MinValue) * - time.Millisecond).String()) - fmt.Println("------") - - calc = calculator.NewInt64(checkAuthorizedResponseHTTPTime) - fmt.Printf("check authorized response avg time: %s\n", (time.Duration(calc.Mean().Register.Mean) * - time.Millisecond).String()) - fmt.Printf("check authorized response max time: %s\n", (time.Duration(calc.Max().Register.MaxValue) * - time.Millisecond).String()) - fmt.Printf("check authorized response min time: %s\n", (time.Duration(calc.Min().Register.MinValue) * - time.Millisecond).String()) - fmt.Println("------") - - calc = calculator.NewInt64(retrieveInteractionsClaimHTTPTime) - fmt.Printf("retrieve claims avg time: %s\n", (time.Duration(calc.Mean().Register.Mean) * - time.Millisecond).String()) - fmt.Printf("retrieve claims max time: %s\n", (time.Duration(calc.Max().Register.MaxValue) * - time.Millisecond).String()) - fmt.Printf("retrieve claims min time: %s\n", (time.Duration(calc.Min().Register.MinValue) * - time.Millisecond).String()) - fmt.Println("------") - - return nil -} - -func getEnv(env, defaultValue string) (string, error) { - str := os.Getenv(env) - if str == "" { - if defaultValue == "" { - return "", fmt.Errorf("env %s is requried", env) - } - - return defaultValue, nil - } - - return str, nil -} diff --git a/test/bdd/pkg/v1/oidc4vp/wallet.go b/test/bdd/pkg/v1/oidc4vp/wallet.go deleted file mode 100644 index 067b12e4a..000000000 --- a/test/bdd/pkg/v1/oidc4vp/wallet.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package oidc4vp - -import ( - "fmt" - - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner" - "github.com/trustbloc/vcs/component/wallet-cli/pkg/walletrunner/vcprovider" -) - -func (e *Steps) createWallet(numOfDIDs int) error { - var err error - e.walletRunner, err = walletrunner.New(vcprovider.ProviderVCS, func(c *vcprovider.Config) { - c.WalletDidCount = numOfDIDs - c.DidMethod = "ion" - }) - if err != nil { - return fmt.Errorf("walletrunner.New: %w", err) - } - - err = e.walletRunner.CreateWallet() - if err != nil { - return fmt.Errorf("walletRunner.CreateWallet: %w", err) - } - - e.bddContext.CredentialSubject = append(e.bddContext.CredentialSubject, e.walletRunner.GetConfig().WalletParams.DidID...) - return nil -} - -func (e *Steps) saveCredentialsInWallet() error { - for _, cred := range e.bddContext.CreatedCredentialsSet { - err := e.walletRunner.SaveCredentialInWallet(cred) - if err != nil { - return fmt.Errorf("wallet add credential failed: %w", err) - } - } - - return nil -} diff --git a/test/stress/go.mod b/test/stress/go.mod index 83a4340a9..bdc55c037 100644 --- a/test/stress/go.mod +++ b/test/stress/go.mod @@ -16,7 +16,7 @@ require ( github.com/samber/lo v1.38.1 github.com/trustbloc/logutil-go v1.0.0-rc1 github.com/trustbloc/vc-go v1.0.3-0.20231117124429-a8a3b24ef734 - github.com/trustbloc/vcs/component/wallet-cli v0.0.0-20231003142611-b9399c5814c2 + github.com/trustbloc/vcs/component/wallet-cli v0.0.0-20231222131742-742a7ae591ba github.com/trustbloc/vcs/test/bdd v0.0.0-00010101000000-000000000000 golang.org/x/oauth2 v0.7.0 )