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/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/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 )