Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sdk): Make credentials optional for GetSubmissionRequirements #577

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions cmd/wallet-sdk-gomobile/credential/inquirer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ package credential

import (
"encoding/json"
"errors"
"fmt"
"net/http"

Expand Down Expand Up @@ -76,7 +75,7 @@ func NewInquirer(opts *InquirerOpts) (*Inquirer, error) {
func (c *Inquirer) GetSubmissionRequirements(query []byte, credentials *verifiable.CredentialsArray,
) (*SubmissionRequirementArray, error) {
if credentials == nil {
return nil, errors.New("credentials must be provided")
credentials = verifiable.NewCredentialsArray()
}

pdQuery, err := unwrapQuery(query)
Expand Down
35 changes: 24 additions & 11 deletions cmd/wallet-sdk-gomobile/credential/inquirer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,30 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) {
require.Equal(t, "VerifiableCredential", desc1.ID)
require.Equal(t, "VerifiableCredential", desc1.Name)
require.Equal(t, "So we can see that you are an expert.", desc1.Purpose)
require.Equal(t, desc1.MatchedVCs.Length(), 4)
require.Equal(t, 4, desc1.MatchedVCs.Length())
require.Equal(t, "", desc1.TypeConstraint())
require.Equal(t, 1, desc1.Schemas().Length())
schema := desc1.Schemas().AtIndex(0)
require.Equal(t, "VerifiableCredential", schema.URI())
require.False(t, schema.Required())
})

t.Run("Success with a nil credentials object", func(t *testing.T) {
query, err := credential.NewInquirer(opts)
require.NoError(t, err)

requirements, err := query.GetSubmissionRequirements(schemaPD, nil)

require.NoError(t, err)
require.Equal(t, requirements.Len(), 1)
req1 := requirements.AtIndex(0)

desc1 := req1.DescriptorAtIndex(0)

require.Equal(t, "VerifiableCredential", desc1.ID)
require.Equal(t, "VerifiableCredential", desc1.Name)
require.Equal(t, "So we can see that you are an expert.", desc1.Purpose)
require.Equal(t, 0, desc1.MatchedVCs.Length())
require.Equal(t, "", desc1.TypeConstraint())
require.Equal(t, 1, desc1.Schemas().Length())
schema := desc1.Schemas().AtIndex(0)
Expand Down Expand Up @@ -192,16 +215,6 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) {

require.Contains(t, err.Error(), "validation of presentation definition failed:")
})

t.Run("Nil credentials", func(t *testing.T) {
query, err := credential.NewInquirer(opts)
require.NoError(t, err)

submissionRequirements, err := query.GetSubmissionRequirements(nil, nil)

require.EqualError(t, err, "credentials must be provided")
require.Nil(t, submissionRequirements)
})
}

func TestInstance_GetSubmissionRequirementsCitizenship(t *testing.T) {
Expand Down
1 change: 0 additions & 1 deletion cmd/wallet-sdk-gomobile/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,6 @@ interaction.presentCredentialUnsafe(preferredVC)
| INVALID_AUTHORIZATION_REQUEST(OVP1-0000) | The authorization request is a URI but specifies a scheme other than "openid-vc".<br/><br/>The authorization request is a URI and is missing the request_uri parameter.<br/><br/>The request object's signature is invalid.<br/><br/>The request object is malformed.<br/><br/>Wallet-SDK does not support the format/type of the authorization request and/or request object. |
| REQUEST_OBJECT_FETCH_FAILED(OVP1-0001) | The authorization request is a URI and the request URI endpoint that it specifies cannot be reached. |
| FAIL_TO_GET_MATCH_REQUIREMENTS_RESULTS(CRQ0-0004) | Invalid presentation definition received from the verifier. |
| NO_CREDENTIAL_SATISFY_REQUIREMENTS(CRQ0-0003) | None of your supplied credentials satisfy the requirements set by the verifier. Make sure you've gone through the full credential matching process correctly. See the OpenID4VP examples above. |
| CREATE_AUTHORIZED_RESPONSE(OVP1-0002) | No credentials provided in the `presentCredential` method call. |
| SEND_AUTHORIZED_RESPONSE(OVP1-0003) | The verifier server rejected your credentials (couldn't be verified, wrong type, etc).<br/><br/>The verifier server is down or incorrectly configured. |

Expand Down
10 changes: 0 additions & 10 deletions demo/app/lib/scenarios/handle_openid_vp_flow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,6 @@ void handleOpenIDVpFlow(BuildContext context, String qrCodeURL) async {
log("stored credentials -> $storedCredentials");

credentials = storedCredentials.map((e) => e.value.rawCredential).toList();
if (credentials.isEmpty) {
log("credentials is empty now $credentials");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomError(
requestErrorTitleMsg: "No Credentials found",
requestErrorSubTitleMsg: "Error found in the presentation flow")));
return;
}

try {
await walletSDKPlugin.processAuthorizationRequest(
Expand Down
8 changes: 0 additions & 8 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ type DIDResolver interface {
Resolve(did string) (*did.DocResolution, error)
}

// A CredentialReader is capable of reading VCs from some underlying storage mechanism.
type CredentialReader interface {
// Get retrieves a VC.
Get(id string) (*verifiable.Credential, error)
// GetAll retrieves all VCs.
GetAll() ([]*verifiable.Credential, error)
}

// A CredentialWriter is capable of writing VCs to some underlying storage mechanism.
type CredentialWriter interface {
// Add adds a VC.
Expand Down
49 changes: 2 additions & 47 deletions pkg/credentialquery/credentialquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ SPDX-License-Identifier: Apache-2.0
package credentialquery

import (
"fmt"

"github.com/piprate/json-gold/ld"
"github.com/trustbloc/vc-go/presexch"
"github.com/trustbloc/vc-go/verifiable"
Expand All @@ -25,10 +23,7 @@ type Instance struct {
}

type queryOpts struct {
// credentials is an array of Verifiable Credentials.
credentials []*verifiable.Credential
// CredentialReader allows for access to a VC storage mechanism.
credentialReader api.CredentialReader

didResolver api.DIDResolver
applySelectiveDisclosure bool
Expand All @@ -37,21 +32,13 @@ type queryOpts struct {
// QueryOpt is the query credential option.
type QueryOpt func(opts *queryOpts)

// WithCredentialsArray sets array of Verifiable Credentials. If specified,
// this takes precedence over the CredentialReader option.
// WithCredentialsArray sets the array of Verifiable Credentials to check against the Presentation Definition.
func WithCredentialsArray(vcs []*verifiable.Credential) QueryOpt {
return func(opts *queryOpts) {
opts.credentials = vcs
}
}

// WithCredentialReader sets credential reader that will be used to fetch credential.
func WithCredentialReader(credentialReader api.CredentialReader) QueryOpt {
return func(opts *queryOpts) {
opts.credentialReader = credentialReader
}
}

// WithSelectiveDisclosure enables selective disclosure apply.
func WithSelectiveDisclosure(didResolver api.DIDResolver) QueryOpt {
return func(opts *queryOpts) {
Expand All @@ -75,11 +62,6 @@ func (c *Instance) GetSubmissionRequirements(
opt(qOpts)
}

credentials, err := getCredentials(qOpts)
if err != nil {
return nil, err
}

var matchOpts []presexch.MatchRequirementsOpt
if qOpts.applySelectiveDisclosure {
matchOpts = append(matchOpts,
Expand All @@ -93,7 +75,7 @@ func (c *Instance) GetSubmissionRequirements(
}

results, err := query.MatchSubmissionRequirement(
credentials,
qOpts.credentials,
c.documentLoader,
matchOpts...,
)
Expand All @@ -108,30 +90,3 @@ func (c *Instance) GetSubmissionRequirements(

return results, nil
}

func getCredentials(qOpts *queryOpts) ([]*verifiable.Credential, error) {
credentials := qOpts.credentials
if len(credentials) == 0 {
if qOpts.credentialReader == nil {
return nil, walleterror.NewValidationError(
module,
CredentialReaderNotSetCode,
CredentialReaderNotSetError,
fmt.Errorf("credentials array or credential reader option must be set"))
}

var err error

credentials, err = qOpts.credentialReader.GetAll()
if err != nil {
return nil,
walleterror.NewValidationError(
module,
CredentialReaderReadFailedCode,
CredentialReaderReadFailedError,
fmt.Errorf("credential reader failed: %w", err))
}
}

return credentials, nil
}
32 changes: 0 additions & 32 deletions pkg/credentialquery/credentialquery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package credentialquery_test
import (
_ "embed"
"encoding/json"
"errors"
"testing"

"github.com/google/uuid"
Expand Down Expand Up @@ -86,24 +85,6 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) {
require.Len(t, requirements[0].Descriptors, 3)
})

t.Run("Reader error", func(t *testing.T) {
instance := credentialquery.NewInstance(docLoader)
_, err := instance.GetSubmissionRequirements(pdQuery, credentialquery.WithCredentialReader(
&readerMock{
err: errors.New("get all error"),
},
))

require.Error(t, err, "credential reader failed: get all error")
})

t.Run("Credentials not provided", func(t *testing.T) {
instance := credentialquery.NewInstance(docLoader)
_, err := instance.GetSubmissionRequirements(pdQuery)

testutil.RequireErrorContains(t, err, "CREDENTIAL_READER_NOT_SET")
})

t.Run("Checks schema", func(t *testing.T) {
incorrectPD := &presexch.PresentationDefinition{ID: uuid.New().String()}

Expand All @@ -117,19 +98,6 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) {
})
}

type readerMock struct {
credentials []*verifiable.Credential
err error
}

func (r *readerMock) Get(string) (*verifiable.Credential, error) {
return nil, r.err
}

func (r *readerMock) GetAll() ([]*verifiable.Credential, error) {
return r.credentials, r.err
}

type didResolverMock struct {
ResolveValue *did.DocResolution
ResolveErr error
Expand Down
14 changes: 1 addition & 13 deletions pkg/credentialquery/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,7 @@ package credentialquery
// nolint:golint,nolintlint
const (
module = "CRQ"
CredentialReaderNotSetError = "CREDENTIAL_READER_NOT_SET" //nolint:gosec //false positive
CredentialReaderReadFailedError = "CREDENTIAL_READER_READ_FAILED" //nolint:gosec //false positive
CreateVPFailedError = "CREATE_VP_FAILED"
NoCredentialSatisfyRequirementsError = "NO_CREDENTIAL_SATISFY_REQUIREMENTS" //nolint:gosec //false positive
FailToGetMatchRequirementsResultsError = "FAIL_TO_GET_MATCH_REQUIREMENTS_RESULTS"
)

// Constants' names and reasons are obvious so they do not require additional comments.
// nolint:golint,nolintlint
const (
CredentialReaderNotSetCode = iota
CredentialReaderReadFailedCode
CreateVPFailedCode
NoCredentialSatisfyRequirementsCode
FailToGetMatchRequirementsResultsCode
)
const FailToGetMatchRequirementsResultsCode = 4 //nolint // Purpose is obvious from the name.
Loading