Skip to content

Commit

Permalink
Have s2s flow take verifier supported DID methods into account (#3374)
Browse files Browse the repository at this point in the history
  • Loading branch information
woutslakhorst committed Sep 17, 2024
1 parent e120fc3 commit 150038a
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 48 deletions.
2 changes: 1 addition & 1 deletion auth/api/iam/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) {
ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/oauth2/verifier").Return(&clientMetadata, nil)
pdEndpoint := "https://example.com/oauth2/verifier/presentation_definition?scope=test"
ctx.iamClient.EXPECT().PresentationDefinition(gomock.Any(), pdEndpoint).Return(&pe.PresentationDefinition{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{holderDID}, nil, pe.PresentationDefinition{}, clientMetadata.VPFormats, gomock.Any()).Return(&vc.VerifiablePresentation{}, &pe.PresentationSubmission{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{holderDID}, nil, pe.PresentationDefinition{}, gomock.Any()).Return(&vc.VerifiablePresentation{}, &pe.PresentationSubmission{}, nil)
ctx.iamClient.EXPECT().PostAuthorizationResponse(gomock.Any(), vc.VerifiablePresentation{}, pe.PresentationSubmission{}, "https://example.com/oauth2/verifier/response", "state").Return("https://example.com/iam/holder/redirect", nil)

res, err := ctx.client.HandleAuthorizeRequest(callCtx, HandleAuthorizeRequestRequestObject{
Expand Down
4 changes: 2 additions & 2 deletions auth/api/iam/openid4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,10 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, subject
// at this point in the flow it would be possible to ask the user to confirm the credentials to use

// all params checked, delegate responsibility to the holder
// todo expiration
buildParams := holder.BuildParams{
Audience: params.get(oauth.ClientIDParam),
Expires: time.Now().Add(15 * time.Minute),
Format: metadata.VPFormats,
Nonce: nonce,
}

Expand All @@ -314,7 +314,7 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, subject
map[did.DID][]vc.VerifiableCredential{userSession.Wallet.DID: userSession.Wallet.Credentials},
)
}
vp, submission, err := targetWallet.BuildSubmission(ctx, []did.DID{walletDID}, nil, *presentationDefinition, metadata.VPFormats, buildParams)
vp, submission, err := targetWallet.BuildSubmission(ctx, []did.DID{walletDID}, nil, *presentationDefinition, buildParams)
if err != nil {
if errors.Is(err, pe.ErrNoCredentials) {
return r.sendAndHandleDirectPostError(ctx, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: fmt.Sprintf("wallet could not fulfill requirements (PD ID: %s, wallet: %s): %s", presentationDefinition.Id, walletDID, err.Error())}, responseURI, state)
Expand Down
4 changes: 2 additions & 2 deletions auth/api/iam/openid4vp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) {
putState(ctx, "state", authzCodeSession)
ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil)
ctx.iamClient.EXPECT().PresentationDefinition(gomock.Any(), pdEndpoint).Return(&pe.PresentationDefinition{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{holderDID}, nil, pe.PresentationDefinition{}, clientMetadata.VPFormats, gomock.Any()).Return(nil, nil, assert.AnError)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{holderDID}, nil, pe.PresentationDefinition{}, gomock.Any()).Return(nil, nil, assert.AnError)
expectPostError(t, ctx, oauth.ServerError, assert.AnError.Error(), responseURI, "state")

_, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderSubjectID, params, pe.WalletOwnerOrganization)
Expand All @@ -353,7 +353,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) {
putState(ctx, "state", authzCodeSession)
ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil)
ctx.iamClient.EXPECT().PresentationDefinition(gomock.Any(), pdEndpoint).Return(&pe.PresentationDefinition{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{holderDID}, nil, pe.PresentationDefinition{}, clientMetadata.VPFormats, gomock.Any()).Return(nil, nil, pe.ErrNoCredentials)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{holderDID}, nil, pe.PresentationDefinition{}, gomock.Any()).Return(nil, nil, pe.ErrNoCredentials)
expectPostError(t, ctx, oauth.InvalidRequest, "wallet could not fulfill requirements (PD ID: , wallet: did:web:example.com:iam:holder): missing credentials", responseURI, "state")

_, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderSubjectID, params, pe.WalletOwnerOrganization)
Expand Down
39 changes: 31 additions & 8 deletions auth/client/iam/openid4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import (
"github.com/nuts-foundation/nuts-node/vcr/credential"
"github.com/nuts-foundation/nuts-node/vdr/didsubject"
"github.com/piprate/json-gold/ld"
"golang.org/x/exp/maps"
"net/http"
"net/url"
"slices"
"time"

"github.com/nuts-foundation/go-did/did"
Expand Down Expand Up @@ -229,7 +231,7 @@ func (c *OpenID4VPClient) AccessToken(ctx context.Context, code string, tokenEnd
}

func (c *OpenID4VPClient) RequestRFC021AccessToken(ctx context.Context, clientID string, subjectID string, authServerURL string, scopes string,
useDPoP bool, credentials []vc.VerifiableCredential) (*oauth.TokenResponse, error) {
useDPoP bool, additionalCredentials []vc.VerifiableCredential) (*oauth.TokenResponse, error) {
iamClient := c.httpClient
metadata, err := c.AuthorizationServerMetadata(ctx, authServerURL)
if err != nil {
Expand All @@ -250,22 +252,43 @@ func (c *OpenID4VPClient) RequestRFC021AccessToken(ctx context.Context, clientID
}

params := holder.BuildParams{
Audience: authServerURL,
Expires: time.Now().Add(time.Second * 5),
Nonce: nutsCrypto.GenerateNonce(),
Audience: authServerURL,
DIDMethods: metadata.DIDMethodsSupported,
Expires: time.Now().Add(time.Second * 5),
Format: metadata.VPFormatsSupported,
Nonce: nutsCrypto.GenerateNonce(),
}

subjectDIDs, err := c.subjectManager.ListDIDs(ctx, subjectID)
if err != nil {
return nil, err
}
additionalCredentials := make(map[did.DID][]vc.VerifiableCredential)

// in the s2s flow we use the metadata to determine the DID methods supported by the verifier
// filter walletDIDs on the DID methods supported by the verifier
j := 0
allMethods := map[string]struct{}{}
for i, d := range subjectDIDs {
allMethods[d.Method] = struct{}{}
if slices.Contains(metadata.DIDMethodsSupported, d.Method) {
subjectDIDs[j] = subjectDIDs[i]
j++
}
}
subjectDIDs = subjectDIDs[:j]

if len(subjectDIDs) == 0 {
return nil, fmt.Errorf("did method mismatch, requested: %v, available: %v", metadata.DIDMethodsSupported, maps.Keys(allMethods))
}

// each additional credential can be used by each DID
additionalWalletCredentials := map[did.DID][]vc.VerifiableCredential{}
for _, subjectDID := range subjectDIDs {
for _, curr := range credentials {
additionalCredentials[subjectDID] = append(additionalCredentials[subjectDID], credential.AutoCorrectSelfAttestedCredential(curr, subjectDID))
for _, curr := range additionalCredentials {
additionalWalletCredentials[subjectDID] = append(additionalWalletCredentials[subjectDID], credential.AutoCorrectSelfAttestedCredential(curr, subjectDID))
}
}
vp, submission, err := c.wallet.BuildSubmission(ctx, subjectDIDs, additionalCredentials, *presentationDefinition, metadata.VPFormatsSupported, params)
vp, submission, err := c.wallet.BuildSubmission(ctx, subjectDIDs, additionalWalletCredentials, *presentationDefinition, params)
if err != nil {
return nil, err
}
Expand Down
32 changes: 21 additions & 11 deletions auth/client/iam/openid4vp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,9 @@ func TestIAMClient_AuthorizationServerMetadata(t *testing.T) {
func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
const subjectID = "subby"
const subjectClientID = "https://example.com/oauth2/subby"
primaryWalletDID := did.MustParseDID("did:primary:123")
secondaryWalletDID := did.MustParseDID("did:secondary:123")
primaryKID := "did:primary:123#1"
primaryWalletDID := did.MustParseDID("did:test:primary")
secondaryWalletDID := did.MustParseDID("did:test:secondary")
primaryKID := "did:test:primary#1"
scopes := "first second"
holderURI := primaryWalletDID.URI()
createdVP := &vc.VerifiablePresentation{
Expand All @@ -249,7 +249,7 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
t.Run("fulfills the Presentation Definition", func(t *testing.T) {
ctx := createClientServerTestContext(t)
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), oauth.DefaultOpenIDSupportedFormats(), gomock.Any()).Return(createdVP, &pe.PresentationSubmission{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), gomock.Any()).Return(createdVP, &pe.PresentationSubmission{}, nil)

response, err := ctx.client.RequestRFC021AccessToken(context.Background(), subjectClientID, subjectID, ctx.verifierURL.String(), scopes, false, nil)

Expand All @@ -261,13 +261,23 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
t.Run("no DID fulfills the Presentation Definition", func(t *testing.T) {
ctx := createClientServerTestContext(t)
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), oauth.DefaultOpenIDSupportedFormats(), gomock.Any()).Return(nil, nil, pe.ErrNoCredentials)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, pe.ErrNoCredentials)

response, err := ctx.client.RequestRFC021AccessToken(context.Background(), subjectClientID, subjectID, ctx.verifierURL.String(), scopes, false, nil)

assert.ErrorIs(t, err, pe.ErrNoCredentials)
assert.Nil(t, response)
})
t.Run("DID not supported", func(t *testing.T) {
ctx := createClientServerTestContext(t)
ctx.authzServerMetadata.DIDMethodsSupported = []string{"other"}
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)

response, err := ctx.client.RequestRFC021AccessToken(context.Background(), subjectClientID, subjectID, ctx.verifierURL.String(), scopes, false, nil)

assert.EqualError(t, err, "did method mismatch, requested: [other], available: [test]")
assert.Nil(t, response)
})
t.Run("with additional credentials", func(t *testing.T) {
ctx := createClientServerTestContext(t)
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)
Expand All @@ -287,8 +297,8 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
},
},
}
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), oauth.DefaultOpenIDSupportedFormats(), gomock.Any()).
DoAndReturn(func(_ context.Context, _ []did.DID, additionalCredentials map[did.DID][]vc.VerifiableCredential, _ pe.PresentationDefinition, _ map[string]map[string][]string, _ holder.BuildParams) (*vc.VerifiablePresentation, *pe.PresentationSubmission, error) {
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), gomock.Any()).
DoAndReturn(func(_ context.Context, _ []did.DID, additionalCredentials map[did.DID][]vc.VerifiableCredential, _ pe.PresentationDefinition, _ holder.BuildParams) (*vc.VerifiablePresentation, *pe.PresentationSubmission, error) {
// Assert self-attested credentials
require.Len(t, additionalCredentials, 2)
require.Len(t, additionalCredentials[primaryWalletDID], 1)
Expand All @@ -310,7 +320,7 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
ctx.keyResolver.EXPECT().ResolveKey(primaryWalletDID, nil, resolver.NutsSigningKeyType).Return(primaryKID, nil, nil)
ctx.jwtSigner.EXPECT().SignDPoP(context.Background(), gomock.Any(), primaryKID).Return("dpop", nil)
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), oauth.DefaultOpenIDSupportedFormats(), gomock.Any()).Return(createdVP, &pe.PresentationSubmission{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), gomock.Any()).Return(createdVP, &pe.PresentationSubmission{}, nil)

response, err := ctx.client.RequestRFC021AccessToken(context.Background(), subjectClientID, subjectID, ctx.verifierURL.String(), scopes, true, nil)

Expand All @@ -332,7 +342,7 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
_, _ = writer.Write(oauthErrorBytes)
}
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), oauth.DefaultOpenIDSupportedFormats(), gomock.Any()).Return(createdVP, &pe.PresentationSubmission{}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), gomock.Any()).Return(createdVP, &pe.PresentationSubmission{}, nil)

_, err := ctx.client.RequestRFC021AccessToken(context.Background(), subjectClientID, subjectID, ctx.verifierURL.String(), scopes, false, nil)

Expand Down Expand Up @@ -375,7 +385,7 @@ func TestRelyingParty_RequestRFC021AccessToken(t *testing.T) {
t.Run("error - failed to build vp", func(t *testing.T) {
ctx := createClientServerTestContext(t)
ctx.subjectManager.EXPECT().ListDIDs(gomock.Any(), subjectID).Return([]did.DID{primaryWalletDID, secondaryWalletDID}, nil)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), oauth.DefaultOpenIDSupportedFormats(), gomock.Any()).Return(nil, nil, assert.AnError)
ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), []did.DID{primaryWalletDID, secondaryWalletDID}, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, assert.AnError)

_, err := ctx.client.RequestRFC021AccessToken(context.Background(), subjectClientID, subjectID, ctx.verifierURL.String(), scopes, false, nil)

Expand Down Expand Up @@ -506,7 +516,7 @@ type clientServerTestContext struct {

func createClientServerTestContext(t *testing.T) *clientServerTestContext {
credentialIssuerMetadata := &oauth.OpenIDCredentialIssuerMetadata{}
metadata := &oauth.AuthorizationServerMetadata{VPFormatsSupported: oauth.DefaultOpenIDSupportedFormats()}
metadata := &oauth.AuthorizationServerMetadata{VPFormatsSupported: oauth.DefaultOpenIDSupportedFormats(), DIDMethodsSupported: []string{"test"}}
ctx := &clientServerTestContext{
clientTestContext: createClientTestContext(t, nil),
metadata: func(writer http.ResponseWriter) {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,5 @@ require (
modernc.org/sqlite v1.32.0 // indirect
rsc.io/qr v0.2.0 // indirect
)

require golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
39 changes: 39 additions & 0 deletions vcr/credential/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"slices"
"time"
)

Expand Down Expand Up @@ -140,3 +141,41 @@ func AutoCorrectSelfAttestedCredential(credential vc.VerifiableCredential, reque
}
return credential
}

// FilterOnDIDMethod filters the credentials based on the DID method of the issuer and credentialSubject.
// credentials are only removed if there's a false match. Nil fields are ignored.
func FilterOnDIDMethod(credentials []vc.VerifiableCredential, didMethods []string) []vc.VerifiableCredential {
// todo: for OpenID4vp there's currently no metadata that passes DID methods to the wallet.
if len(didMethods) == 0 {
return credentials
}
var result []vc.VerifiableCredential
outer:
for _, credential := range credentials {
// check issuer
issuerDID, err := did.ParseDID(credential.Issuer.String())
if err == nil {
if !slices.Contains(didMethods, issuerDID.Method) {
continue
}
}
bl := make([]BaseCredentialSubject, 0)
err = credential.UnmarshalCredentialSubject(&bl)
if err != nil {
continue
}
for _, b := range bl {
if b.ID != "" {
// check credentialSubject
subjectDID, err := did.ParseDID(b.ID)
if err == nil {
if !slices.Contains(didMethods, subjectDID.Method) {
continue outer
}
}
}
}
result = append(result, credential)
}
return result
}
104 changes: 104 additions & 0 deletions vcr/credential/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,107 @@ func TestAutoCorrectSelfAttestedCredential(t *testing.T) {
assert.NotEqual(t, "", result.ID.String())
assert.Equal(t, requestor.String(), result.CredentialSubject[0].(map[string]interface{})["id"])
}

func TestFilterOnDIDMethod(t *testing.T) {
t.Run("valid", func(t *testing.T) {
credentials := []vc.VerifiableCredential{
{
Issuer: ssi.MustParseURI("did:test:123"),
CredentialSubject: []interface{}{
map[string]interface{}{"id": ssi.MustParseURI("did:test:456")},
},
},
}

result := FilterOnDIDMethod(credentials, []string{"test"})

assert.Len(t, result, 1)
})
t.Run("no match on issuer", func(t *testing.T) {
credentials := []vc.VerifiableCredential{
{
Issuer: ssi.MustParseURI("did:test:123"),
},
}

result := FilterOnDIDMethod(credentials, []string{"other"})

assert.Len(t, result, 0)
})
t.Run("no match on credentialSubject", func(t *testing.T) {
credentials := []vc.VerifiableCredential{
{
CredentialSubject: []interface{}{
map[string]interface{}{"id": ssi.MustParseURI("did:test:456")},
},
},
}

result := FilterOnDIDMethod(credentials, []string{"other"})

assert.Len(t, result, 0)
})
t.Run("issuer not a DID", func(t *testing.T) {
credentials := []vc.VerifiableCredential{
{
Issuer: ssi.MustParseURI("client_id"),
},
}

result := FilterOnDIDMethod(credentials, []string{"test"})

assert.Len(t, result, 1)
})
t.Run("credentialSubject not a did", func(t *testing.T) {
credentials := []vc.VerifiableCredential{
{
CredentialSubject: []interface{}{
map[string]interface{}{"id": ssi.MustParseURI("client_id")},
},
},
}

result := FilterOnDIDMethod(credentials, []string{"test"})

assert.Len(t, result, 1)
})
bug := `
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1",
"https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json"
],
"credentialSubject": {
"id": "did:web:nodeB:iam:f17d021a-f0e6-4ed9-a22a-e4447f1720ba",
"organization": {
"city": "Caretown",
"name": "Caresoft B.V."
}
},
"id": "did:web:nodeB:iam:f17d021a-f0e6-4ed9-a22a-e4447f1720ba#36b83620-13b2-4fbf-a57a-618731839a6f",
"issuanceDate": "2024-09-17T06:44:57.97676521Z",
"issuer": "did:web:nodeB:iam:f17d021a-f0e6-4ed9-a22a-e4447f1720ba",
"proof": {
"created": "2024-09-17T06:44:57.97676521Z",
"jws": "eyJhbGciOiJFUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImtpZCI6ImRpZDp3ZWI6bm9kZUI6aWFtOmYxN2QwMjFhLWYwZTYtNGVkOS1hMjJhLWU0NDQ3ZjE3MjBiYSM3YTExZTIxZi05ZWYwLTQ5NTctOTM4Zi0xZTUyZTE5NTc1M2QifQ..4t6XiCFkvl3swZkpN63xvRUcTwMj5uUP2wVpkAmQXg33Jaou6JEgi_gwnygTK1C_bfwaqa6X7cMvT5Fm0cwTEA",
"proofPurpose": "assertionMethod",
"type": "JsonWebSignature2020",
"verificationMethod": "did:web:nodeB:iam:f17d021a-f0e6-4ed9-a22a-e4447f1720ba#7a11e21f-9ef0-4957-938f-1e52e195753d"
},
"type": [
"NutsOrganizationCredential",
"VerifiableCredential"
]
}
`
t.Run("foo", func(t *testing.T) {
c := vc.VerifiableCredential{}
_ = c.UnmarshalJSON([]byte(bug))
credentials := []vc.VerifiableCredential{c}

result := FilterOnDIDMethod(credentials, []string{"web"})

assert.Len(t, result, 1)
})
}
Loading

0 comments on commit 150038a

Please sign in to comment.